create-pmos 0.49.0 → 0.50.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 +8 -6
- package/workspace/run-tests.js +148 -0
- package/workspace/server.js +5 -2
- package/workspace/test-files.json +180 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.50.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-05T02:52:09Z"}
|
|
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-05T02:52:09Z"}
|
|
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-05T02:52:09Z"}
|
|
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.50.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",
|
|
@@ -9,15 +9,17 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"start": "node server.js",
|
|
12
|
-
"test": "node
|
|
13
|
-
"test:modules": "node
|
|
12
|
+
"test": "node run-tests.js unit",
|
|
13
|
+
"test:modules": "node run-tests.js modules",
|
|
14
14
|
"test:ui": "node ui-test.js",
|
|
15
15
|
"test:parity": "node design-parity.js --board=WsA",
|
|
16
16
|
"test:parity:four-panel": "node design-parity.js --states",
|
|
17
|
-
"test:all": "
|
|
17
|
+
"test:all": "node run-tests.js all && npm run test:parity && npm run test:offline",
|
|
18
18
|
"doctor": "node doctor.js",
|
|
19
|
-
"test:panels": "node
|
|
20
|
-
"test:offline": "node offline-tarball.test.js"
|
|
19
|
+
"test:panels": "node run-tests.js panels",
|
|
20
|
+
"test:offline": "node offline-tarball.test.js",
|
|
21
|
+
"test:browser": "node run-tests.js browser",
|
|
22
|
+
"test:list-check": "node run-tests.js --check"
|
|
21
23
|
},
|
|
22
24
|
"engines": {
|
|
23
25
|
"node": ">=18"
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
/* run-tests.js — run the workspace suites side by side.
|
|
4
|
+
*
|
|
5
|
+
* node run-tests.js <group> [--jobs=N] [--shard=I/N] [--list] [--verbose]
|
|
6
|
+
* node run-tests.js --check
|
|
7
|
+
*
|
|
8
|
+
* Groups come from test-files.json: unit (test.js + modules), modules, panels, ui, browser
|
|
9
|
+
* (panels + ui), all. The npm scripts call this file, so `npm test` and CI run the same lists.
|
|
10
|
+
*
|
|
11
|
+
* Running in parallel is safe because every suite claims its own ephemeral port, scratch state
|
|
12
|
+
* directory and private tmux socket (test-run.js, or the same three lines written out in the
|
|
13
|
+
* suite). It was checked rather than assumed: on 2026-10-05 all 48 browser suites ran four at a
|
|
14
|
+
* time with no failure that the serial run did not also show.
|
|
15
|
+
*
|
|
16
|
+
* Nothing is skipped. --shard splits a group across CI jobs; the shards partition the list (every
|
|
17
|
+
* file in exactly one shard), and --check fails when a suite on disk is in no list, so a new suite
|
|
18
|
+
* that nobody wired in is a red build rather than a suite that runs nowhere.
|
|
19
|
+
*
|
|
20
|
+
* Output: a suite's own output is buffered and printed whole when it finishes, so lines from two
|
|
21
|
+
* suites never interleave. A passing suite prints its last lines; --verbose prints all of it.
|
|
22
|
+
*/
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const os = require('os');
|
|
25
|
+
const path = require('path');
|
|
26
|
+
const { spawn } = require('child_process');
|
|
27
|
+
|
|
28
|
+
const DIR = __dirname;
|
|
29
|
+
const FILES = JSON.parse(fs.readFileSync(path.join(DIR, 'test-files.json'), 'utf8'));
|
|
30
|
+
const GROUPS = {
|
|
31
|
+
unit: [...FILES.unit, ...FILES.modules],
|
|
32
|
+
modules: FILES.modules,
|
|
33
|
+
panels: FILES.panels,
|
|
34
|
+
ui: FILES.ui,
|
|
35
|
+
browser: [...FILES.panels, ...FILES.ui],
|
|
36
|
+
all: [...FILES.unit, ...FILES.modules, ...FILES.panels, ...FILES.ui],
|
|
37
|
+
};
|
|
38
|
+
// Suites that run elsewhere on purpose, so --check does not count them as unwired.
|
|
39
|
+
const ELSEWHERE = {
|
|
40
|
+
'offline-tarball.test.js': 'npm run test:offline (needs a packed tarball)',
|
|
41
|
+
};
|
|
42
|
+
const PER_FILE_TIMEOUT_MS = 10 * 60 * 1000;
|
|
43
|
+
|
|
44
|
+
const args = process.argv.slice(2);
|
|
45
|
+
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); };
|
|
46
|
+
const group = args.find((a) => !a.startsWith('--'));
|
|
47
|
+
|
|
48
|
+
const secs = (f) => Number((FILES.seconds || {})[f]) || 10;
|
|
49
|
+
// Slowest first, so a long suite never starts last and holds the run open on its own.
|
|
50
|
+
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);
|
|
51
|
+
|
|
52
|
+
// Deal the suites, slowest first, each to the shard with the least time so far (ties to the lower
|
|
53
|
+
// shard). Deterministic, so every CI job computes the same split, and a partition by construction:
|
|
54
|
+
// each suite goes to exactly one shard. --check proves the partition for every shard count it tries.
|
|
55
|
+
function shardOf(list, spec) {
|
|
56
|
+
const m = /^(\d+)\/(\d+)$/.exec(String(spec));
|
|
57
|
+
if (!m) throw new Error(`--shard must look like 1/4, got "${spec}"`);
|
|
58
|
+
const i = Number(m[1]); const n = Number(m[2]);
|
|
59
|
+
if (n < 1 || i < 1 || i > n) throw new Error(`--shard ${spec}: the index must be between 1 and ${n}`);
|
|
60
|
+
const load = new Array(n).fill(0); const out = Array.from({ length: n }, () => []);
|
|
61
|
+
for (const f of bySlowest(list)) {
|
|
62
|
+
let best = 0; for (let k = 1; k < n; k++) if (load[k] < load[best]) best = k;
|
|
63
|
+
out[best].push(f); load[best] += secs(f);
|
|
64
|
+
}
|
|
65
|
+
return out[i - 1];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function check() {
|
|
69
|
+
const listed = new Set(GROUPS.all);
|
|
70
|
+
const problems = [];
|
|
71
|
+
const dupes = GROUPS.all.filter((f, k) => GROUPS.all.indexOf(f) !== k);
|
|
72
|
+
for (const f of new Set(dupes)) problems.push(`${f} is listed more than once in test-files.json`);
|
|
73
|
+
for (const f of listed) if (!fs.existsSync(path.join(DIR, f))) problems.push(`${f} is listed in test-files.json but does not exist`);
|
|
74
|
+
for (const f of FILES.exclusive || []) if (!listed.has(f)) problems.push(`${f} is marked exclusive but is in no test list`);
|
|
75
|
+
const onDisk = fs.readdirSync(DIR).filter((f) => f === 'test.js' || /(\.test\.(js|mjs)|-test\.js)$/.test(f));
|
|
76
|
+
for (const f of onDisk) if (!listed.has(f) && !ELSEWHERE[f]) problems.push(`${f} is a suite on disk that no test list runs. Add it to test-files.json.`);
|
|
77
|
+
for (const n of [2, 3, 4, 6, 8]) for (const g of ['unit', 'browser']) {
|
|
78
|
+
const parts = Array.from({ length: n }, (_, k) => shardOf(GROUPS[g], `${k + 1}/${n}`)).flat();
|
|
79
|
+
if (parts.length !== GROUPS[g].length || new Set(parts).size !== GROUPS[g].length) problems.push(`--shard does not partition "${g}" into ${n}`);
|
|
80
|
+
}
|
|
81
|
+
if (problems.length) { for (const p of problems) console.error(`✗ ${p}`); process.exitCode = 1; return; }
|
|
82
|
+
console.log(`✓ ${listed.size} suites listed, each once, each on disk; ${onDisk.length - Object.keys(ELSEWHERE).length} suites on disk, all listed; shards partition every group.`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function runOne(file, verbose) {
|
|
86
|
+
return new Promise((resolve) => {
|
|
87
|
+
const t0 = Date.now();
|
|
88
|
+
const chunks = [];
|
|
89
|
+
const child = spawn(process.execPath, [file], { cwd: DIR, env: process.env, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
90
|
+
child.stdout.on('data', (d) => chunks.push(d));
|
|
91
|
+
child.stderr.on('data', (d) => chunks.push(d));
|
|
92
|
+
let timedOut = false;
|
|
93
|
+
const timer = setTimeout(() => { timedOut = true; child.kill('SIGKILL'); }, PER_FILE_TIMEOUT_MS);
|
|
94
|
+
child.on('close', (code, signal) => {
|
|
95
|
+
clearTimeout(timer);
|
|
96
|
+
const secs = ((Date.now() - t0) / 1000).toFixed(1);
|
|
97
|
+
const out = Buffer.concat(chunks).toString('utf8').replace(/\s+$/, '');
|
|
98
|
+
const ok = code === 0 && !timedOut;
|
|
99
|
+
const why = timedOut ? `timed out after ${PER_FILE_TIMEOUT_MS / 60000} min` : (signal ? `killed by ${signal}` : `exit ${code}`);
|
|
100
|
+
const lines = out.split('\n');
|
|
101
|
+
const body = ok && !verbose ? lines.slice(-3).join('\n') : out;
|
|
102
|
+
process.stdout.write(`\n── ${ok ? 'PASS' : 'FAIL'} ${file} (${secs}s${ok ? '' : ', ' + why}) ──\n${body}\n`);
|
|
103
|
+
resolve({ file, ok, secs: Number(secs), why });
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function main() {
|
|
109
|
+
if (flag('check')) return check();
|
|
110
|
+
if (!group || !GROUPS[group]) {
|
|
111
|
+
console.error(`usage: node run-tests.js <${Object.keys(GROUPS).join('|')}> [--jobs=N] [--shard=I/N] [--list] [--verbose]\n node run-tests.js --check`);
|
|
112
|
+
process.exitCode = 2; return;
|
|
113
|
+
}
|
|
114
|
+
let list = GROUPS[group];
|
|
115
|
+
const shard = flag('shard');
|
|
116
|
+
list = shard ? shardOf(list, shard) : bySlowest(list);
|
|
117
|
+
if (flag('list')) { console.log(list.join('\n')); return; }
|
|
118
|
+
const cpus = typeof os.availableParallelism === 'function' ? os.availableParallelism() : os.cpus().length;
|
|
119
|
+
// A browser suite runs a server, a tmux daemon and a Chromium of its own, and several of them assert
|
|
120
|
+
// on timing. At one per CPU they starved each other: 2 of 48 failed per run on a 4-CPU machine, a
|
|
121
|
+
// different 2 each time, all green alone. Half the CPUs for any group with browser suites in it
|
|
122
|
+
// kept every run green. --jobs or PMOS_TEST_JOBS still overrides.
|
|
123
|
+
const hasBrowser = list.some((f) => FILES.panels.includes(f) || FILES.ui.includes(f));
|
|
124
|
+
const auto = hasBrowser ? Math.max(1, Math.floor(cpus / 2)) : cpus;
|
|
125
|
+
const jobs = Math.max(1, Number(flag('jobs')) || Number(process.env.PMOS_TEST_JOBS) || auto);
|
|
126
|
+
const verbose = !!flag('verbose');
|
|
127
|
+
console.log(`run-tests: ${group}${shard ? ` shard ${shard}` : ''} — ${list.length} suites, ${jobs} at a time`);
|
|
128
|
+
|
|
129
|
+
const t0 = Date.now();
|
|
130
|
+
const results = [];
|
|
131
|
+
// An exclusive suite writes the checkout's own state, so it runs alone: first the shared suites side
|
|
132
|
+
// by side, then each exclusive one with nothing beside it.
|
|
133
|
+
const exclusive = new Set(FILES.exclusive || []);
|
|
134
|
+
const shared = list.filter((f) => !exclusive.has(f));
|
|
135
|
+
let next = 0;
|
|
136
|
+
const worker = async () => { while (next < shared.length) { const f = shared[next++]; results.push(await runOne(f, verbose)); } };
|
|
137
|
+
await Promise.all(Array.from({ length: Math.min(jobs, shared.length) }, worker));
|
|
138
|
+
for (const f of list.filter((x) => exclusive.has(x))) results.push(await runOne(f, verbose));
|
|
139
|
+
|
|
140
|
+
const failed = results.filter((r) => !r.ok);
|
|
141
|
+
const slowest = [...results].sort((a, b) => b.secs - a.secs).slice(0, 5).map((r) => `${r.file} ${r.secs}s`).join(', ');
|
|
142
|
+
console.log(`\nrun-tests: ${results.length - failed.length}/${results.length} suites passed in ${((Date.now() - t0) / 1000).toFixed(1)}s wall. Slowest: ${slowest}.`);
|
|
143
|
+
// exitCode, never process.exit(): stdout to a pipe is asynchronous, and exiting here cut the CI log
|
|
144
|
+
// off mid-suite, before the summary that names what failed.
|
|
145
|
+
if (failed.length) { console.log(`FAILED: ${failed.map((r) => `${r.file} (${r.why})`).join(', ')}`); process.exitCode = 1; }
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
main().catch((e) => { console.error(e); process.exitCode = 1; });
|
package/workspace/server.js
CHANGED
|
@@ -2296,8 +2296,11 @@ function checkDueSchedules() {
|
|
|
2296
2296
|
} catch {}
|
|
2297
2297
|
return fired;
|
|
2298
2298
|
}
|
|
2299
|
-
// the tick — the persistent server checks on an interval; unref'd so it never blocks exit
|
|
2300
|
-
|
|
2299
|
+
// the tick — the persistent server checks on an interval; unref'd so it never blocks exit.
|
|
2300
|
+
// PMOS_TICK_MS exists for the suites (same footing as PMOS_LIVENESS_MS): proactive-prepare.test.js
|
|
2301
|
+
// used to wait on two real minutes of this clock. Unset, the interval is the 60 seconds it always was.
|
|
2302
|
+
const TICK_MS = Math.max(200, Number(process.env.PMOS_TICK_MS) || 60000);
|
|
2303
|
+
try { const _tick = setInterval(() => { checkDueSchedules(); proactiveTick(); try { prepareTick({ manual: false }); } catch { /* the next tick tries again */ } }, TICK_MS); if (_tick.unref) _tick.unref(); } catch {}
|
|
2301
2304
|
|
|
2302
2305
|
// ── Sessions: real PTYs, owned by the server, with disk-backed scrollback + metadata. ──
|
|
2303
2306
|
/** @type {Map<string, {id,ws,name,agent,cwd,state,note,created,pty,buf,cols,rows,clients:Set}>} */
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "The test files each npm test script runs. run-tests.js reads this file; add a new suite here and every runner, local and CI, picks it up. Every suite claims its own port, state directory and tmux socket (test-run.js or its own equivalent), which is what makes running them side by side safe.",
|
|
3
|
+
"_comment_exclusive": "Suites that write the checkout's own state (workspace/state/, the home workspace's ledgers) while they run. run-tests.js runs each of them with nothing else beside it, because suites that read the checkout as their home workspace (proactive.test.js's scan, for one) would see those writes mid-run.",
|
|
4
|
+
"exclusive": [
|
|
5
|
+
"test.js"
|
|
6
|
+
],
|
|
7
|
+
"unit": [
|
|
8
|
+
"test.js"
|
|
9
|
+
],
|
|
10
|
+
"modules": [
|
|
11
|
+
"proactive.test.js",
|
|
12
|
+
"focus.test.js",
|
|
13
|
+
"handoff.test.js",
|
|
14
|
+
"repo-scan.test.js",
|
|
15
|
+
"profiles.test.js",
|
|
16
|
+
"external-linking.test.js",
|
|
17
|
+
"story-artifacts.test.js",
|
|
18
|
+
"no-network.test.js",
|
|
19
|
+
"typesafe-validation.test.js",
|
|
20
|
+
"claims.test.js",
|
|
21
|
+
"status-facts-panel.test.js",
|
|
22
|
+
"jev-claims.test.js",
|
|
23
|
+
"jev-categorize.test.js",
|
|
24
|
+
"jev-status.test.js",
|
|
25
|
+
"jev-route.test.js",
|
|
26
|
+
"repo-watch.test.js",
|
|
27
|
+
"fixture-parity.test.mjs",
|
|
28
|
+
"markdown.test.mjs",
|
|
29
|
+
"start-policy.test.js",
|
|
30
|
+
"state-isolation.test.js",
|
|
31
|
+
"sync.test.js",
|
|
32
|
+
"schedule-cli.test.js",
|
|
33
|
+
"initiative-surface.test.js",
|
|
34
|
+
"guide.test.js",
|
|
35
|
+
"next-step-actions.test.js",
|
|
36
|
+
"kg.test.js",
|
|
37
|
+
"doc-category.test.js",
|
|
38
|
+
"doc-category-corrections.test.js",
|
|
39
|
+
"proactive-prepare.test.js",
|
|
40
|
+
"learn.test.js",
|
|
41
|
+
"session-flag.test.js",
|
|
42
|
+
"live-notes.test.js"
|
|
43
|
+
],
|
|
44
|
+
"panels": [
|
|
45
|
+
"pm-shell-test.js",
|
|
46
|
+
"pm-repos-test.js",
|
|
47
|
+
"pm-initiatives-test.js",
|
|
48
|
+
"pm-detail-test.js",
|
|
49
|
+
"pm-tabs-test.js",
|
|
50
|
+
"pm-term-test.js",
|
|
51
|
+
"pm-modes-test.js",
|
|
52
|
+
"pm-r1-test.js",
|
|
53
|
+
"pm-r2-test.js",
|
|
54
|
+
"pm-r3-test.js",
|
|
55
|
+
"pm-r4-test.js",
|
|
56
|
+
"pm-r5-test.js",
|
|
57
|
+
"pm-r6-test.js",
|
|
58
|
+
"pm-r7-test.js",
|
|
59
|
+
"pm-r8-test.js",
|
|
60
|
+
"pm-r9-test.js",
|
|
61
|
+
"pm-r10-test.js",
|
|
62
|
+
"pm-r11-test.js",
|
|
63
|
+
"pm-r12-test.js",
|
|
64
|
+
"pm-r13-test.js",
|
|
65
|
+
"pm-r14-test.js",
|
|
66
|
+
"pm-r15-test.js",
|
|
67
|
+
"pm-r16-test.js",
|
|
68
|
+
"pm-r17-test.js",
|
|
69
|
+
"pm-r18-test.js",
|
|
70
|
+
"pm-r19-test.js",
|
|
71
|
+
"pm-r20-test.js",
|
|
72
|
+
"pm-r21-test.js",
|
|
73
|
+
"pm-r22-test.js",
|
|
74
|
+
"pm-r23-test.js",
|
|
75
|
+
"pm-r24-test.js",
|
|
76
|
+
"pm-r25-test.js",
|
|
77
|
+
"pm-r26-test.js",
|
|
78
|
+
"pm-doc-category-test.js",
|
|
79
|
+
"pm-r27-test.js",
|
|
80
|
+
"pm-r28-test.js",
|
|
81
|
+
"pm-r29-today-test.js",
|
|
82
|
+
"pm-r29-portfolio-test.js",
|
|
83
|
+
"pm-r29-knowledge-test.js",
|
|
84
|
+
"pm-r29-memory-test.js",
|
|
85
|
+
"pm-r29-skills-scheduled-test.js",
|
|
86
|
+
"pm-r29-validate-guide-test.js",
|
|
87
|
+
"pm-r29-reports-addrepo-test.js",
|
|
88
|
+
"pm-r29-filespad-test.js",
|
|
89
|
+
"pm-r29-newsession-alignment-test.js",
|
|
90
|
+
"pm-live-notes-test.js",
|
|
91
|
+
"pm-r30-test.js"
|
|
92
|
+
],
|
|
93
|
+
"ui": [
|
|
94
|
+
"ui-test.js"
|
|
95
|
+
],
|
|
96
|
+
"seconds": {
|
|
97
|
+
"test.js": 48,
|
|
98
|
+
"proactive.test.js": 4,
|
|
99
|
+
"focus.test.js": 1,
|
|
100
|
+
"handoff.test.js": 1,
|
|
101
|
+
"repo-scan.test.js": 1,
|
|
102
|
+
"profiles.test.js": 1,
|
|
103
|
+
"external-linking.test.js": 1,
|
|
104
|
+
"story-artifacts.test.js": 1,
|
|
105
|
+
"no-network.test.js": 1,
|
|
106
|
+
"typesafe-validation.test.js": 1,
|
|
107
|
+
"claims.test.js": 1,
|
|
108
|
+
"status-facts-panel.test.js": 1,
|
|
109
|
+
"jev-claims.test.js": 3,
|
|
110
|
+
"jev-categorize.test.js": 1,
|
|
111
|
+
"jev-status.test.js": 1,
|
|
112
|
+
"jev-route.test.js": 1,
|
|
113
|
+
"repo-watch.test.js": 1,
|
|
114
|
+
"fixture-parity.test.mjs": 1,
|
|
115
|
+
"markdown.test.mjs": 1,
|
|
116
|
+
"start-policy.test.js": 1,
|
|
117
|
+
"state-isolation.test.js": 4,
|
|
118
|
+
"sync.test.js": 4,
|
|
119
|
+
"schedule-cli.test.js": 3,
|
|
120
|
+
"initiative-surface.test.js": 3,
|
|
121
|
+
"guide.test.js": 1,
|
|
122
|
+
"next-step-actions.test.js": 5,
|
|
123
|
+
"kg.test.js": 1,
|
|
124
|
+
"doc-category.test.js": 1,
|
|
125
|
+
"doc-category-corrections.test.js": 1,
|
|
126
|
+
"proactive-prepare.test.js": 9,
|
|
127
|
+
"learn.test.js": 3,
|
|
128
|
+
"session-flag.test.js": 1,
|
|
129
|
+
"live-notes.test.js": 3,
|
|
130
|
+
"pm-shell-test.js": 6,
|
|
131
|
+
"pm-repos-test.js": 3,
|
|
132
|
+
"pm-initiatives-test.js": 15,
|
|
133
|
+
"pm-detail-test.js": 16,
|
|
134
|
+
"pm-tabs-test.js": 59,
|
|
135
|
+
"pm-term-test.js": 28,
|
|
136
|
+
"pm-modes-test.js": 18,
|
|
137
|
+
"pm-r1-test.js": 6,
|
|
138
|
+
"pm-r2-test.js": 3,
|
|
139
|
+
"pm-r3-test.js": 3,
|
|
140
|
+
"pm-r4-test.js": 4,
|
|
141
|
+
"pm-r5-test.js": 3,
|
|
142
|
+
"pm-r6-test.js": 2,
|
|
143
|
+
"pm-r7-test.js": 1,
|
|
144
|
+
"pm-r8-test.js": 1,
|
|
145
|
+
"pm-r9-test.js": 2,
|
|
146
|
+
"pm-r10-test.js": 1,
|
|
147
|
+
"pm-r11-test.js": 1,
|
|
148
|
+
"pm-r12-test.js": 6,
|
|
149
|
+
"pm-r13-test.js": 2,
|
|
150
|
+
"pm-r14-test.js": 7,
|
|
151
|
+
"pm-r15-test.js": 35,
|
|
152
|
+
"pm-r16-test.js": 7,
|
|
153
|
+
"pm-r17-test.js": 3,
|
|
154
|
+
"pm-r18-test.js": 4,
|
|
155
|
+
"pm-r19-test.js": 18,
|
|
156
|
+
"pm-r20-test.js": 4,
|
|
157
|
+
"pm-r21-test.js": 4,
|
|
158
|
+
"pm-r22-test.js": 32,
|
|
159
|
+
"pm-r23-test.js": 13,
|
|
160
|
+
"pm-r24-test.js": 4,
|
|
161
|
+
"pm-r25-test.js": 9,
|
|
162
|
+
"pm-r26-test.js": 9,
|
|
163
|
+
"pm-doc-category-test.js": 4,
|
|
164
|
+
"pm-r27-test.js": 26,
|
|
165
|
+
"pm-r28-test.js": 9,
|
|
166
|
+
"pm-r29-today-test.js": 4,
|
|
167
|
+
"pm-r29-portfolio-test.js": 3,
|
|
168
|
+
"pm-r29-knowledge-test.js": 3,
|
|
169
|
+
"pm-r29-memory-test.js": 4,
|
|
170
|
+
"pm-r29-skills-scheduled-test.js": 6,
|
|
171
|
+
"pm-r29-validate-guide-test.js": 20,
|
|
172
|
+
"pm-r29-reports-addrepo-test.js": 4,
|
|
173
|
+
"pm-r29-filespad-test.js": 5,
|
|
174
|
+
"pm-r29-newsession-alignment-test.js": 8,
|
|
175
|
+
"pm-live-notes-test.js": 24,
|
|
176
|
+
"pm-r30-test.js": 4,
|
|
177
|
+
"ui-test.js": 30
|
|
178
|
+
},
|
|
179
|
+
"_comment_seconds": "Measured wall time of each suite run alone (4-CPU sandbox, 2026-10-05). Only the ORDER uses it: the slowest suites start first, and --shard deals suites to the least-loaded shard so the shards finish together. A wrong or missing number never skips a suite; a missing one counts as 10."
|
|
180
|
+
}
|