create-pmos 0.52.0 → 0.53.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.
Files changed (212) hide show
  1. package/package.json +1 -1
  2. package/payload/KIT-VERSION +2 -2
  3. package/payload/mods/pmos-cockpit/README.md +13 -0
  4. package/payload/mods/pmos-cockpit/hooks/lib/commands.mjs +2 -1
  5. package/payload/mods/pmos-cockpit/hooks/lib/grants.mjs +41 -11
  6. package/payload/mods/pmos-cockpit/hooks/lib/guard.mjs +228 -8
  7. package/payload/mods/pmos-cockpit/hooks/register.tsx +11 -3
  8. package/payload/planning/evals/eval-calibration.md +1 -1
  9. package/payload/planning/okrs/health-budgets.md +1 -1
  10. package/payload/scripts/plan-items-stage.js +271 -0
  11. package/payload/scripts/pmos-log.sh +18 -7
  12. package/payload/scripts/workflow-state.js +28 -2
  13. package/payload/state/discoveries.md +1 -1
  14. package/payload/state/log.md +1 -1
  15. package/workspace/bin/pmos.js +15 -1
  16. package/workspace/cut-stub.js +40 -0
  17. package/workspace/first-paint-audit.js +145 -0
  18. package/workspace/fixtures/capture/requirements/capture-2026-10-04-1.md +10 -0
  19. package/workspace/fixtures/initiatives-api/list.json +176 -0
  20. package/workspace/fixtures/initiatives-api/post-403.json +5 -0
  21. package/workspace/fixtures/initiatives-api/post-ok.json +10 -0
  22. package/workspace/fixtures/initiatives-api/roadmap.json +208 -0
  23. package/workspace/fixtures/initiatives-api/sheet.json +112 -0
  24. package/workspace/fixtures/report-api/draft.json +177 -0
  25. package/workspace/fixtures/reports-page/draft-biweekly-2026-10-16.md +97 -0
  26. package/workspace/fixtures/today-api/provider-rows.json +204 -0
  27. package/workspace/fixtures/today-api/seam-probe-kinds.json +5 -0
  28. package/workspace/guide/board.md +81 -0
  29. package/workspace/guide/breakdown.md +154 -0
  30. package/workspace/guide/command-palette.md +75 -0
  31. package/workspace/guide/index.md +27 -17
  32. package/workspace/guide/initiatives-roadmap.md +130 -0
  33. package/workspace/guide/initiatives-screen.md +89 -0
  34. package/workspace/guide/initiatives-timeline.md +57 -0
  35. package/workspace/guide/integrated-work.md +94 -0
  36. package/workspace/guide/knowledge-graph.md +3 -68
  37. package/workspace/guide/layout.md +98 -32
  38. package/workspace/guide/live-notes.md +3 -3
  39. package/workspace/guide/pm-only-acts.md +89 -0
  40. package/workspace/guide/pm-only-lines.md +61 -0
  41. package/workspace/guide/reports-page.md +80 -0
  42. package/workspace/guide/reports.md +73 -0
  43. package/workspace/guide/timeline.md +70 -0
  44. package/workspace/guide/today.md +85 -0
  45. package/workspace/guide/validate-with-typesafe.md +1 -1
  46. package/workspace/lib/actor.js +91 -0
  47. package/workspace/lib/board-cache.js +32 -2
  48. package/workspace/lib/board-cli.js +108 -0
  49. package/workspace/lib/board-fold.js +232 -0
  50. package/workspace/lib/breakdown-export.js +80 -0
  51. package/workspace/lib/capture.js +98 -0
  52. package/workspace/lib/engine/plan-items-stage.js +271 -0
  53. package/workspace/lib/engine/workflow-state.js +28 -2
  54. package/workspace/lib/factory-join.js +158 -0
  55. package/workspace/lib/grant-mint.js +113 -0
  56. package/workspace/lib/health-rules-factory.js +92 -0
  57. package/workspace/lib/health-rules-plan.js +164 -0
  58. package/workspace/lib/health.js +79 -0
  59. package/workspace/lib/initiative-cli.js +222 -0
  60. package/workspace/lib/initiative-display.js +183 -0
  61. package/workspace/lib/initiative-needs.js +127 -0
  62. package/workspace/lib/initiative-rows.js +206 -0
  63. package/workspace/lib/initiative-writer.js +141 -0
  64. package/workspace/lib/issues.js +206 -0
  65. package/workspace/lib/item-cli.js +195 -0
  66. package/workspace/lib/item-spans.js +137 -0
  67. package/workspace/lib/measure-cli.js +100 -0
  68. package/workspace/lib/measurements.js +189 -0
  69. package/workspace/lib/mobile-nav.js +57 -53
  70. package/workspace/lib/needs-providers.js +120 -0
  71. package/workspace/lib/placements.js +440 -0
  72. package/workspace/lib/plan-items.js +922 -0
  73. package/workspace/lib/plan-ledgers.js +85 -0
  74. package/workspace/lib/plan-rules.js +72 -0
  75. package/workspace/lib/plan-tree.js +367 -0
  76. package/workspace/lib/pm-guard.js +174 -0
  77. package/workspace/lib/pm-lines-needs.js +331 -0
  78. package/workspace/lib/pmos-verbs.js +66 -0
  79. package/workspace/lib/proactive.js +7 -4
  80. package/workspace/lib/report-cli.js +152 -0
  81. package/workspace/lib/report-draft.js +579 -0
  82. package/workspace/lib/report-needs.js +213 -0
  83. package/workspace/lib/route-modules.js +128 -0
  84. package/workspace/lib/routes-board.js +91 -0
  85. package/workspace/lib/routes-capture.js +52 -0
  86. package/workspace/lib/routes-initiatives.js +371 -0
  87. package/workspace/lib/routes-reports.js +163 -0
  88. package/workspace/lib/routes-timeline.js +197 -0
  89. package/workspace/lib/routes-today.js +32 -0
  90. package/workspace/lib/today-cli.js +99 -0
  91. package/workspace/lib/today.js +333 -0
  92. package/workspace/lib/triage.js +163 -0
  93. package/workspace/lib/verify-work-cli.js +81 -0
  94. package/workspace/lib/work-check-needs.js +99 -0
  95. package/workspace/package-lock.json +1 -1
  96. package/workspace/package.json +4 -5
  97. package/workspace/public/index.html +29 -19
  98. package/workspace/public/pm/app.js +299 -30
  99. package/workspace/public/pm/bd-board.js +113 -0
  100. package/workspace/public/pm/bd-drag.js +59 -0
  101. package/workspace/public/pm/bd-model.js +152 -0
  102. package/workspace/public/pm/bd-timeline.js +233 -0
  103. package/workspace/public/pm/bd-tree.js +244 -0
  104. package/workspace/public/pm/board-copy.js +57 -0
  105. package/workspace/public/pm/breakdown-api.js +58 -0
  106. package/workspace/public/pm/breakdown-copy.js +138 -0
  107. package/workspace/public/pm/components.css +3054 -0
  108. package/workspace/public/pm/cycle-review.js +125 -0
  109. package/workspace/public/pm/data.js +40 -187
  110. package/workspace/public/pm/dest-board.js +438 -0
  111. package/workspace/public/pm/dest-initiatives.js +134 -0
  112. package/workspace/public/pm/dest-reports.js +270 -0
  113. package/workspace/public/pm/dest-settings.js +217 -0
  114. package/workspace/public/pm/dest-today.js +176 -0
  115. package/workspace/public/pm/dialogs.js +88 -0
  116. package/workspace/public/pm/display-word.js +56 -0
  117. package/workspace/public/pm/dock.js +105 -0
  118. package/workspace/public/pm/fixture.js +8 -227
  119. package/workspace/public/pm/frame-copy.js +94 -0
  120. package/workspace/public/pm/handoff-box.js +82 -0
  121. package/workspace/public/pm/http.js +88 -0
  122. package/workspace/public/pm/icons.js +53 -0
  123. package/workspace/public/pm/initiative-list.js +218 -0
  124. package/workspace/public/pm/initiative-sheet.js +261 -0
  125. package/workspace/public/pm/initiatives-api.js +59 -0
  126. package/workspace/public/pm/initiatives-copy.js +215 -0
  127. package/workspace/public/pm/initiatives-timeline-copy.js +59 -0
  128. package/workspace/public/pm/initiatives-timeline.js +410 -0
  129. package/workspace/public/pm/initiatives-ui.js +123 -0
  130. package/workspace/public/pm/item-sheet.js +251 -0
  131. package/workspace/public/pm/keep-card.js +204 -0
  132. package/workspace/public/pm/lifecycle.js +7 -1
  133. package/workspace/public/pm/mobile.css +49 -20
  134. package/workspace/public/pm/mobile.js +157 -29
  135. package/workspace/public/pm/modes-live.js +7 -883
  136. package/workspace/public/pm/modes.js +32 -1024
  137. package/workspace/public/pm/new-initiative-sheet.js +74 -0
  138. package/workspace/public/pm/next-step-ui.js +3 -2
  139. package/workspace/public/pm/palette-copy.js +51 -0
  140. package/workspace/public/pm/palette.js +312 -0
  141. package/workspace/public/pm/panel-breakdown.js +702 -0
  142. package/workspace/public/pm/panel-cockpit.js +63 -295
  143. package/workspace/public/pm/panel-detail.js +25 -172
  144. package/workspace/public/pm/panel-initiatives.js +1 -19
  145. package/workspace/public/pm/panel-repos.js +31 -81
  146. package/workspace/public/pm/panel-tabs.js +197 -110
  147. package/workspace/public/pm/panel-trees.js +5 -2
  148. package/workspace/public/pm/portfolio-report.js +61 -0
  149. package/workspace/public/pm/primitives.js +656 -0
  150. package/workspace/public/pm/print.css +33 -0
  151. package/workspace/public/pm/provider-cards.js +136 -0
  152. package/workspace/public/pm/registry.js +194 -0
  153. package/workspace/public/pm/release-card.js +110 -0
  154. package/workspace/public/pm/report-view.js +309 -0
  155. package/workspace/public/pm/reports-copy.js +114 -0
  156. package/workspace/public/pm/roadmap.js +271 -0
  157. package/workspace/public/pm/sig-watch.js +41 -0
  158. package/workspace/public/pm/state.js +8 -4
  159. package/workspace/public/pm/status-banner.js +13 -5
  160. package/workspace/public/pm/tab-doc.js +2 -4
  161. package/workspace/public/pm/tab-term.js +12 -2
  162. package/workspace/public/pm/tab-web.js +3 -2
  163. package/workspace/public/pm/timeline-copy.js +48 -0
  164. package/workspace/public/pm/today-cards.js +244 -0
  165. package/workspace/public/pm/today-copy.js +97 -0
  166. package/workspace/public/pm/tokens.css +16 -18
  167. package/workspace/public/pm/topbar.js +102 -0
  168. package/workspace/public/pm/triage-card.js +60 -0
  169. package/workspace/public/pm/undo-hold.js +103 -0
  170. package/workspace/public/pm/view-addrepo.js +2 -1
  171. package/workspace/public/pm/view-livenotes.js +3 -1
  172. package/workspace/public/pm/view-today.js +7 -162
  173. package/workspace/public/vendor/Sortable.min.js +2 -0
  174. package/workspace/public/vendor/lucide-sprite.js +79 -0
  175. package/workspace/replaced-tests.json +2864 -0
  176. package/workspace/run-tests.js +56 -5
  177. package/workspace/server.js +312 -36
  178. package/workspace/sheet-answer.js +61 -0
  179. package/workspace/skills/measure.skill +41 -0
  180. package/workspace/skills/report-biweekly.skill +23 -25
  181. package/workspace/skills/tracker-refresh.skill +50 -0
  182. package/workspace/test-files.json +19 -3
  183. package/workspace/test-lists/base.json +6 -0
  184. package/workspace/test-lists/run2-a.json +6 -0
  185. package/workspace/test-lists/run2-b.json +13 -0
  186. package/workspace/test-lists/run2-c.json +19 -0
  187. package/workspace/test-lists/run2-d.json +22 -0
  188. package/workspace/test-lists/run3-0.json +5 -0
  189. package/workspace/test-lists/run3-a.json +5 -0
  190. package/workspace/test-lists/run3-b.json +6 -0
  191. package/workspace/test-lists/run3-c.json +15 -0
  192. package/workspace/test-lists/run3-d.json +6 -0
  193. package/workspace/test-lists/run4-a.json +8 -0
  194. package/workspace/test-lists/run4-b.json +6 -0
  195. package/workspace/test-lists/run4-c.json +13 -0
  196. package/workspace/test-lists/run4-d.json +9 -0
  197. package/workspace/test-lists/run5-a.json +6 -0
  198. package/workspace/test-lists/run5-b.json +6 -0
  199. package/workspace/test-lists/run5-c.json +15 -0
  200. package/workspace/test-run.js +27 -1
  201. package/workspace/third-party/README.md +44 -0
  202. package/workspace/third-party/lucide-static-1.52.0.LICENSE +43 -0
  203. package/workspace/third-party/sortablejs-1.15.7.LICENSE +21 -0
  204. package/workspace/design-parity.js +0 -673
  205. package/workspace/public/pm/tab-task.js +0 -86
  206. package/workspace/public/pm/view-guide.js +0 -141
  207. package/workspace/public/pm/view-knowledge.js +0 -225
  208. package/workspace/public/pm/view-memory.js +0 -122
  209. package/workspace/public/pm/view-portfolio.js +0 -153
  210. package/workspace/public/pm/view-reports.js +0 -190
  211. package/workspace/public/pm/view-scheduled.js +0 -161
  212. package/workspace/public/pm/view-skills.js +0 -216
@@ -24,6 +24,13 @@
24
24
  *
25
25
  * Output: a suite's own output is buffered and printed whole when it finishes, so lines from two
26
26
  * suites never interleave. A passing suite prints its last lines; --verbose prints all of it.
27
+ *
28
+ * Fragments (pm-os-absorb Runs 2 to 5). Builders who work at the same time add their suites in a file
29
+ * of their own, test-lists/<name>.json, never in test-files.json: two slices that each append to the
30
+ * same JSON array always conflict. A fragment has the keys modules, panels, exclusive and seconds of
31
+ * test-files.json, plus `helpers` (a -test.js file that is a helper, not a suite, with the reason, as
32
+ * in ELSEWHERE). This file merges every fragment into the lists before it does anything else, so
33
+ * --check, --shard and every group see one list. A fragment that is not valid fails every command.
27
34
  */
28
35
  const fs = require('fs');
29
36
  const os = require('os');
@@ -31,7 +38,41 @@ const path = require('path');
31
38
  const { spawn } = require('child_process');
32
39
 
33
40
  const DIR = __dirname;
34
- const FILES = JSON.parse(fs.readFileSync(path.join(DIR, 'test-files.json'), 'utf8'));
41
+ const FRAG_DIR = path.join(DIR, 'test-lists');
42
+ const FRAG_KEYS = ['_comment', 'modules', 'panels', 'exclusive', 'seconds', 'helpers'];
43
+ const fragProblems = [];
44
+ const fragHelpers = {};
45
+ /** test-files.json with every test-lists/*.json fragment merged in (see the header). Problems go to fragProblems. */
46
+ function loadFiles() {
47
+ const files = JSON.parse(fs.readFileSync(path.join(DIR, 'test-files.json'), 'utf8'));
48
+ let names = [];
49
+ try { names = fs.readdirSync(FRAG_DIR).filter((n) => n.endsWith('.json')).sort(); } catch { /* no fragments yet */ }
50
+ for (const n of names) {
51
+ const where = `test-lists/${n}`;
52
+ if (!/^[a-z0-9][a-z0-9-]*\.json$/.test(n)) fragProblems.push(`${where}: a fragment is named <lowercase-id>.json, for example run2-a.json`);
53
+ let frag;
54
+ try { frag = JSON.parse(fs.readFileSync(path.join(FRAG_DIR, n), 'utf8')); } catch (e) { fragProblems.push(`${where} is not valid JSON — ${e.message}`); continue; }
55
+ if (!frag || typeof frag !== 'object' || Array.isArray(frag)) { fragProblems.push(`${where} is not a JSON object`); continue; }
56
+ for (const k of Object.keys(frag)) if (!FRAG_KEYS.includes(k)) fragProblems.push(`${where}: unknown key "${k}" (the keys are ${FRAG_KEYS.slice(1).join(', ')})`);
57
+ for (const k of ['modules', 'panels', 'exclusive']) {
58
+ if (frag[k] === undefined) continue;
59
+ if (!Array.isArray(frag[k]) || !frag[k].every((f) => typeof f === 'string')) { fragProblems.push(`${where}: "${k}" is a list of file names`); continue; }
60
+ files[k] = [...(files[k] || []), ...frag[k]];
61
+ }
62
+ for (const [f, v] of Object.entries(frag.seconds || {})) {
63
+ if (typeof v !== 'number' || !(v > 0)) fragProblems.push(`${where}: seconds of ${f} is a positive number`);
64
+ else if (files.seconds && f in files.seconds) fragProblems.push(`${where}: seconds of ${f} is already recorded elsewhere`);
65
+ else files.seconds = { ...(files.seconds || {}), [f]: v };
66
+ }
67
+ for (const [f, why] of Object.entries(frag.helpers || {})) {
68
+ if (typeof why !== 'string' || !why.trim()) fragProblems.push(`${where}: helper ${f} needs a reason`);
69
+ else if (fragHelpers[f]) fragProblems.push(`${where}: helper ${f} is already named by ${fragHelpers[f].where}`);
70
+ else fragHelpers[f] = { why, where };
71
+ }
72
+ }
73
+ return files;
74
+ }
75
+ const FILES = loadFiles();
35
76
  const GROUPS = {
36
77
  unit: [...FILES.unit, ...FILES.modules],
37
78
  modules: FILES.modules,
@@ -43,7 +84,14 @@ const GROUPS = {
43
84
  // Suites that run elsewhere on purpose, so --check does not count them as unwired.
44
85
  const ELSEWHERE = {
45
86
  'offline-tarball.test.js': 'npm run test:offline (needs a packed tarball)',
87
+ // A helper module, not a suite: the Breakdown suites require it (pm-os-absorb Run 1). Its name keeps it out of the
88
+ // tarball (BUNDLE_EXCLUDES has 'pm-*-test.js'), which is why it ends in -test.js.
89
+ 'pm-breakdown-stub-test.js': 'not a suite — the helpers pm-breakdown-test.js, pm-breakdown-phone-test.js and first-paint-count.test.js require',
46
90
  };
91
+ for (const [f, h] of Object.entries(fragHelpers)) {
92
+ if (ELSEWHERE[f]) fragProblems.push(`${h.where}: helper ${f} is already exempt in run-tests.js`);
93
+ else ELSEWHERE[f] = `${h.why} (${h.where})`;
94
+ }
47
95
  const PER_FILE_TIMEOUT_MS = 10 * 60 * 1000;
48
96
 
49
97
  const args = process.argv.slice(2);
@@ -119,10 +167,11 @@ function changedSuites(list, ref) {
119
167
 
120
168
  function check() {
121
169
  const listed = new Set(GROUPS.all);
122
- const problems = [];
170
+ const problems = [...fragProblems];
123
171
  const dupes = GROUPS.all.filter((f, k) => GROUPS.all.indexOf(f) !== k);
124
- for (const f of new Set(dupes)) problems.push(`${f} is listed more than once in test-files.json`);
125
- 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`);
172
+ for (const f of new Set(dupes)) problems.push(`${f} is listed more than once in test-files.json and test-lists/`);
173
+ for (const f of listed) if (!fs.existsSync(path.join(DIR, f))) problems.push(`${f} is listed in test-files.json or test-lists/ but does not exist`);
174
+ for (const f of Object.keys(ELSEWHERE)) if (listed.has(f)) problems.push(`${f} is exempt as a helper but is also listed as a suite`);
126
175
  for (const f of FILES.exclusive || []) if (!listed.has(f)) problems.push(`${f} is marked exclusive but is in no test list`);
127
176
  const onDisk = fs.readdirSync(DIR).filter((f) => f === 'test.js' || /(\.test\.(js|mjs)|-test\.js)$/.test(f));
128
177
  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.`);
@@ -131,7 +180,8 @@ function check() {
131
180
  if (parts.length !== GROUPS[g].length || new Set(parts).size !== GROUPS[g].length) problems.push(`--shard does not partition "${g}" into ${n}`);
132
181
  }
133
182
  if (problems.length) { for (const p of problems) console.error(`✗ ${p}`); process.exitCode = 1; return; }
134
- 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.`);
183
+ const frags = fs.existsSync(FRAG_DIR) ? fs.readdirSync(FRAG_DIR).filter((n) => n.endsWith('.json')).length : 0;
184
+ console.log(`✓ ${listed.size} suites listed, each once, each on disk; ${onDisk.filter((f) => !ELSEWHERE[f]).length} suites on disk, all listed; shards partition every group; ${frags} fragment(s) in test-lists/.`);
135
185
  }
136
186
 
137
187
  function runOne(file, verbose) {
@@ -159,6 +209,7 @@ function runOne(file, verbose) {
159
209
 
160
210
  async function main() {
161
211
  if (flag('check')) return check();
212
+ if (fragProblems.length) { for (const p of fragProblems) console.error(`✗ ${p}`); process.exitCode = 2; return; }
162
213
  if (!group || !GROUPS[group]) {
163
214
  console.error(`usage: node run-tests.js <${Object.keys(GROUPS).join('|')}> [--jobs=N] [--shard=I/N] [--list] [--verbose]\n node run-tests.js --check`);
164
215
  process.exitCode = 2; return;