create-pmos 0.511.0 → 0.513.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 (114) hide show
  1. package/package.json +27 -31
  2. package/payload/KIT-VERSION +1 -1
  3. package/payload/planning/evals/eval-calibration.md +1 -1
  4. package/payload/planning/okrs/health-budgets.md +1 -1
  5. package/payload/state/discoveries.md +1 -1
  6. package/workspace/briefing-premium-adddoc-test.js +3 -3
  7. package/workspace/briefing-premium-board-test.js +2 -2
  8. package/workspace/briefing-premium-initiative-test.js +3 -3
  9. package/workspace/briefing-premium-insights-test.js +2 -2
  10. package/workspace/briefing-premium-sources-test.js +4 -5
  11. package/workspace/briefing-premium-test.js +2 -2
  12. package/workspace/changelog.json +26 -0
  13. package/workspace/fixtures/next-prompt-replay.json +40 -0
  14. package/workspace/guide/home.md +1 -1
  15. package/workspace/guide/index.md +1 -0
  16. package/workspace/guide/next-prompt.md +62 -0
  17. package/workspace/guide/settings.md +3 -1
  18. package/workspace/intel-report-ui-test.js +1 -1
  19. package/workspace/intelligence-gaps-ui-test.js +1 -1
  20. package/workspace/lib/next-prompt.js +199 -0
  21. package/workspace/lib/premium-console-type.js +15 -12
  22. package/workspace/lib/route-modules.js +1 -0
  23. package/workspace/lib/routes-next-prompt.js +248 -0
  24. package/workspace/metric-drafts-ui-test.js +2 -2
  25. package/workspace/package-lock.json +2 -2
  26. package/workspace/package.json +1 -1
  27. package/workspace/premium-menu-rows-test.js +1 -1
  28. package/workspace/premium-vw-doc-test.js +1 -1
  29. package/workspace/public/pm/bd-pin-card.css +4 -4
  30. package/workspace/public/pm/bd-publish.css +11 -11
  31. package/workspace/public/pm/bd-shared.css +17 -17
  32. package/workspace/public/pm/c3-capture.css +9 -9
  33. package/workspace/public/pm/c3-initiatives.css +23 -23
  34. package/workspace/public/pm/c3-listen.css +1 -1
  35. package/workspace/public/pm/components.css +572 -572
  36. package/workspace/public/pm/console3.css +374 -373
  37. package/workspace/public/pm/cozy-adler.css +4 -4
  38. package/workspace/public/pm/cozy-aud.css +1 -1
  39. package/workspace/public/pm/cozy-board.css +328 -328
  40. package/workspace/public/pm/cozy-ev.css +80 -78
  41. package/workspace/public/pm/cozy-home-rows.css +32 -32
  42. package/workspace/public/pm/cozy-home.css +285 -241
  43. package/workspace/public/pm/cozy-insights.css +200 -200
  44. package/workspace/public/pm/cozy-live-pill.css +5 -5
  45. package/workspace/public/pm/cozy-mtg-present.css +28 -28
  46. package/workspace/public/pm/cozy-onboard.css +15 -15
  47. package/workspace/public/pm/cozy-panel.css +39 -39
  48. package/workspace/public/pm/cozy-pick.css +5 -5
  49. package/workspace/public/pm/cozy-rec-now.css +23 -23
  50. package/workspace/public/pm/cozy-rec-present.css +11 -11
  51. package/workspace/public/pm/cozy-rec-tab.css +13 -13
  52. package/workspace/public/pm/cozy-settings.js +37 -2
  53. package/workspace/public/pm/cozy-steps.css +14 -14
  54. package/workspace/public/pm/cozy-today.css +95 -95
  55. package/workspace/public/pm/cozy-viewer.css +27 -27
  56. package/workspace/public/pm/cozy-vw-mtg.css +113 -113
  57. package/workspace/public/pm/cozy-vw-rp.css +79 -79
  58. package/workspace/public/pm/cozy-vw-shell.css +43 -43
  59. package/workspace/public/pm/cozy-vw.css +129 -129
  60. package/workspace/public/pm/cozy-xv.css +15 -15
  61. package/workspace/public/pm/cozy.css +53 -21
  62. package/workspace/public/pm/dash-view.css +27 -27
  63. package/workspace/public/pm/hi-pop.css +26 -26
  64. package/workspace/public/pm/home-chat.js +21 -4
  65. package/workspace/public/pm/home-impact.css +8 -8
  66. package/workspace/public/pm/home-ini.css +36 -36
  67. package/workspace/public/pm/home-line.css +7 -7
  68. package/workspace/public/pm/impact.css +9 -9
  69. package/workspace/public/pm/ini-view.css +243 -243
  70. package/workspace/public/pm/load-shapes.css +1 -1
  71. package/workspace/public/pm/loose-props.css +14 -14
  72. package/workspace/public/pm/metric-drafts.css +20 -20
  73. package/workspace/public/pm/ms-suggest.css +9 -9
  74. package/workspace/public/pm/next-prompt-ui.js +117 -0
  75. package/workspace/public/pm/ov-view.css +14 -14
  76. package/workspace/public/pm/premium-insights.js +3 -3
  77. package/workspace/public/pm/premium-report-frame.css +1 -1
  78. package/workspace/public/pm/premium-report-intel.css +17 -17
  79. package/workspace/public/pm/premium-report-print.css +1 -1
  80. package/workspace/public/pm/premium-vw-backlog.css +13 -13
  81. package/workspace/public/pm/premium-vw-cal.css +32 -32
  82. package/workspace/public/pm/premium-vw-copy.css +7 -7
  83. package/workspace/public/pm/premium-vw-doc.css +69 -69
  84. package/workspace/public/pm/premium-vw-item.css +12 -12
  85. package/workspace/public/pm/premium-vw-jira.css +39 -39
  86. package/workspace/public/pm/premium-vw-meet.css +28 -28
  87. package/workspace/public/pm/premium-vw-notepad.css +13 -13
  88. package/workspace/public/pm/premium-vw-notes.css +11 -11
  89. package/workspace/public/pm/premium-vw-rec.css +27 -27
  90. package/workspace/public/pm/premium-vw-report.css +21 -21
  91. package/workspace/public/pm/premium-vw-slides.css +28 -28
  92. package/workspace/public/pm/premium.css +618 -618
  93. package/workspace/public/pm/prog-order.css +6 -6
  94. package/workspace/public/pm/prog-page.css +17 -17
  95. package/workspace/public/pm/program-charter.css +33 -33
  96. package/workspace/public/pm/ready-lib.css +6 -6
  97. package/workspace/public/pm/sort-order.css +1 -1
  98. package/workspace/public/pm/split-merge.css +18 -18
  99. package/workspace/public/pm/task-card.css +24 -24
  100. package/workspace/public/pm/tk-tasks.css +42 -42
  101. package/workspace/public/pm/tk-view.css +6 -6
  102. package/workspace/public/pm/wf-page.css +31 -31
  103. package/workspace/public/premium-aud.html +1 -1
  104. package/workspace/public/premium-board.html +1 -1
  105. package/workspace/public/premium-intelligence.html +1 -1
  106. package/workspace/public/premium-rec.html +1 -1
  107. package/workspace/public/premium-viewer.html +1 -1
  108. package/workspace/public/premium-workflows.html +1 -1
  109. package/workspace/public/premium.html +1 -1
  110. package/workspace/public/vendor/plus-jakarta-sans-var.woff2 +0 -0
  111. package/workspace/server.js +1 -0
  112. package/workspace/test-lists/next-prompt.json +5 -0
  113. package/workspace/third-party/README.md +2 -1
  114. package/workspace/third-party/plus-jakarta-sans-OFL.LICENSE +93 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-pmos",
3
- "version": "0.511.0",
3
+ "version": "0.513.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": {
@@ -32,6 +32,32 @@
32
32
  "audit"
33
33
  ],
34
34
  "pmosChanges": [
35
+ {
36
+ "v": "0.513.0",
37
+ "at": "2026-11-03T15:46:14.696Z",
38
+ "f": 4,
39
+ "prs": [
40
+ {
41
+ "n": 1203,
42
+ "t": "Typography roles with Plus Jakarta Sans, two weights, four sizes",
43
+ "s": "Typography roles with Plus Jakarta Sans, two weights, four sizes",
44
+ "k": "new"
45
+ }
46
+ ]
47
+ },
48
+ {
49
+ "v": "0.512.0",
50
+ "at": "2026-11-03T15:33:10.629Z",
51
+ "f": 4,
52
+ "prs": [
53
+ {
54
+ "n": 1204,
55
+ "t": "Suggest the next prompt and sharpen a rough one in the chat",
56
+ "s": "Suggest the next prompt and sharpen a rough one in the chat",
57
+ "k": "new"
58
+ }
59
+ ]
60
+ },
35
61
  {
36
62
  "v": "0.511.0",
37
63
  "at": "2026-11-03T15:10:29.930Z",
@@ -536,36 +562,6 @@
536
562
  "d": "On a phone the initiative page had uneven edges. Now everything lines up on one even margin."
537
563
  }
538
564
  ]
539
- },
540
- {
541
- "v": "0.493.0",
542
- "at": "2026-11-02T14:41:05.438Z",
543
- "f": 4,
544
- "prs": [
545
- {
546
- "n": 1152,
547
- "t": "Retire Now / Next / Later; the Roadmap is one ranked list",
548
- "s": "Retire Now / Next / Later; the Roadmap is one ranked list",
549
- "k": "new",
550
- "h": "One ranked roadmap",
551
- "d": "The roadmap split work into Now, Next and Later. Now it is one ranked list, and each initiative's stage says where it is."
552
- }
553
- ]
554
- },
555
- {
556
- "v": "0.492.0",
557
- "at": "2026-11-02T10:50:33.055Z",
558
- "f": 4,
559
- "prs": [
560
- {
561
- "n": 1155,
562
- "t": "A new user's empty PMOS folder no longer skips the setup conversation",
563
- "s": "A new user's empty PMOS folder no longer skips the setup conversation",
564
- "k": "fix",
565
- "h": "New users get the setup conversation",
566
- "d": "A new user with an empty PMOS folder landed on a bare Home. Now the setup conversation starts as it should."
567
- }
568
- ]
569
565
  }
570
566
  ]
571
567
  }
@@ -1,3 +1,3 @@
1
- kernel-source: pmos@f8d9c6e4
1
+ kernel-source: pmos@d90291c5
2
2
  assembled: 2026-11-03
3
3
  mode: vendor
@@ -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-11-03T15:10:26Z"}
5
+ generated: {by: process:create-pmos, at: "2026-11-03T15:46:12Z"}
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-11-03T15:10:26Z"}
5
+ generated: {by: process:create-pmos, at: "2026-11-03T15:46:12Z"}
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-11-03T15:10:26Z"}
5
+ generated: {by: process:create-pmos, at: "2026-11-03T15:46:12Z"}
6
6
  status: stable
7
7
  ---
8
8
 
@@ -145,7 +145,7 @@ const PAGES = {
145
145
  ok('A3 the add route takes POST only', wrong === 405);
146
146
  }
147
147
 
148
- /* A8 fidelity: the form follows the Agreed design (premium-agreed-design): the Viewer is one cozy card in Nunito, its presses pills, the main one
148
+ /* A8 fidelity: the form follows the Agreed design (premium-agreed-design): the Viewer is one cozy card in Plus Jakarta Sans, its presses pills, the main one
149
149
  * the solid colour. The colours are read from the page's own tokens, so a theme is checked against itself. */
150
150
  for (const [w, h] of [[1280, 900], [390, 844]]) for (const th of ['light', 'dark']) {
151
151
  const R = await open('initiative', { w, h, theme: th });
@@ -159,8 +159,8 @@ const PAGES = {
159
159
  return { vw: cs('#czViewer').backgroundColor, card: tok('--cz-card', 'background-color'), vwR: cs('#czViewer').borderTopLeftRadius, ff: cs('#czViewer').fontFamily, go: [cs(go).backgroundColor, '700', cs(go).borderTopLeftRadius], solid: tok('--cz-solid', 'background-color'), no: bk ? getComputedStyle(bk).backgroundColor : 'rgba(0, 0, 0, 0)', dlg: !!(document.querySelector('#dlg') && !!document.querySelector('#dlg.on')) };
160
160
  });
161
161
  const d = [];
162
- if (!/^Nunito/.test(btn.ff) || btn.br !== '999px') d.push('button ' + JSON.stringify(btn));
163
- if (g.vw !== g.card || !/^Nunito/.test(g.ff) || (w > 760 && parseFloat(g.vwR) < 20)) d.push('viewer ' + [g.vw, g.card, g.vwR, g.ff].join(' '));
162
+ if (!/^"?Plus Jakarta Sans/.test(btn.ff) || btn.br !== '999px') d.push('button ' + JSON.stringify(btn));
163
+ if (g.vw !== g.card || !/^"?Plus Jakarta Sans/.test(g.ff) || (w > 760 && parseFloat(g.vwR) < 20)) d.push('viewer ' + [g.vw, g.card, g.vwR, g.ff].join(' '));
164
164
  if (g.go[0] !== g.solid || +g.go[1] < 700 || g.go[2] !== '999px') d.push('Add document ' + g.go.join(' ') + ' want ' + g.solid);
165
165
  if (g.no !== g.card && g.no !== 'rgba(0, 0, 0, 0)') d.push('Back ' + g.no); // ← cancels (batch 18 item 2-C): the header's quiet round press
166
166
  if (g.dlg) d.push('the old dialog opened');
@@ -645,11 +645,11 @@ const IVREAD = () => {
645
645
  await R.ctx.close();
646
646
  }
647
647
 
648
- /* B13 the Agreed design's look: Nunito, the soft panel, the chips and the track's rows */
648
+ /* B13 the Agreed design's look: Plus Jakarta Sans, the soft panel, the chips and the track's rows */
649
649
  for (const th of ['light', 'dark']) {
650
650
  const R = await open({ w: 1440, h: 900, theme: th }), q = R.page;
651
651
  const s = await q.evaluate(() => { const cs = (sel) => getComputedStyle(document.querySelector(sel)); const main = cs('main.bd-main'), row = document.querySelector('.bt-l').getBoundingClientRect(); return { font: cs('body').fontFamily, rad: main.borderTopLeftRadius, bg: main.backgroundColor, chip: Math.round(document.querySelector('#chJira').getBoundingClientRect().height), row: Math.round(row.height), tab: cs('#tabIni').fontWeight, fab: cs('#czFab').borderTopLeftRadius }; });
652
- ok(`B13 the look of the Agreed design at 1440 px, ${th}: Nunito, a 28 px panel, 30 px chips, 54 px rows`, /^"?Nunito/.test(s.font) && s.rad === '28px' && s.chip === 30 && s.row >= 54 && s.row <= 62 && s.tab === '700' && (th === 'light' ? s.bg === 'rgb(255, 255, 255)' : s.bg !== 'rgb(255, 255, 255)'), JSON.stringify(s));
652
+ ok(`B13 the look of the Agreed design at 1440 px, ${th}: Plus Jakarta Sans, a 28 px panel, 30 px chips, 54 px rows`, /^"?Plus Jakarta Sans/.test(s.font) && s.rad === '28px' && s.chip === 30 && s.row >= 54 && s.row <= 62 && s.tab === '600' && (th === 'light' ? s.bg === 'rgb(255, 255, 255)' : s.bg !== 'rgb(255, 255, 255)'), JSON.stringify(s));
653
653
  await R.ctx.close();
654
654
  }
655
655
  return finish();
@@ -252,7 +252,7 @@ const WRITES = /^\/api\/(?:boards\/(?:move|tasks\/(?:add|state))|lifecycle|core-
252
252
  const post = await fetch(BASE + '/api/initiative-premium?id=x', { method: 'POST' }); ok('I10 the route answers GET only', post.status === 405);
253
253
  }
254
254
 
255
- /* I12 fidelity: the Agreed design (premium-agreed-design, Inside one initiative): Nunito, the cozy card, the CTA in the solid colour, the floating
255
+ /* I12 fidelity: the Agreed design (premium-agreed-design, Inside one initiative): Plus Jakarta Sans, the cozy card, the CTA in the solid colour, the floating
256
256
  * menu. The colours are read from the page's own tokens, so each theme is checked against itself. */
257
257
  for (const [w, h] of [[1440, 900], [390, 844]]) for (const th of ['light', 'dark']) {
258
258
  const R = await open({ w, h, theme: th });
@@ -264,7 +264,7 @@ const WRITES = /^\/api\/(?:boards\/(?:move|tasks\/(?:add|state))|lifecycle|core-
264
264
  return { font: cs('body').fontFamily, cta: (cs('#czViewer .iv-go') || {}).backgroundColor, ctaAlt: !document.querySelector('#czViewer .iv-go'), solid: tok('--cz-solid'), blue: tok('--cz-blue'), well: tok('--cz-well'), card: tok('--cz-card'), box: cs('#czViewer .iv-mc').backgroundColor, rad: parseFloat(cs(innerWidth <= 760 ? '#czViewer .iv-tel' : '#czViewer .iv-mc').borderTopLeftRadius), fab: fr ? [Math.round(fr.width), Math.round(fr.height)] : null, over: document.documentElement.scrollWidth - innerWidth, cardW: Math.round(c.width) };
265
265
  });
266
266
  const d = [];
267
- if (!/^Nunito/.test(g.font)) d.push('font ' + g.font);
267
+ if (!/^"?Plus Jakarta Sans/.test(g.font)) d.push('font ' + g.font);
268
268
  if (!g.ctaAlt && ![g.solid, g.blue].includes(g.cta)) d.push('cta ' + g.cta + ' want ' + g.solid + ' or ' + g.blue);
269
269
  if (![g.well, g.card].includes(g.box) && !/rgba/.test(g.box)) d.push('card box ' + g.box);
270
270
  // a phone has no wrapper card (batch 23 item 5, canvas PmA): the mission is the first card there
@@ -272,7 +272,7 @@ const WRITES = /^\/api\/(?:boards\/(?:move|tasks\/(?:add|state))|lifecycle|core-
272
272
  // on a phone the initiative's page is the Viewer, full screen over the list: the floating menu waits under it (HbCPhone)
273
273
  if (w > 760 && (!g.fab || g.fab[0] !== 56 || g.fab[1] !== 56)) d.push('fab ' + JSON.stringify(g.fab));
274
274
  if (g.over > 0) d.push('sideways scroll ' + g.over);
275
- ok(`I12 the initiative follows the Agreed design at ${w} px, ${th}: Nunito, the cozy card, the CTA, the floating menu`, d.length === 0, d.join(' ; '));
275
+ ok(`I12 the initiative follows the Agreed design at ${w} px, ${th}: Plus Jakarta Sans, the cozy card, the CTA, the floating menu`, d.length === 0, d.join(' ; '));
276
276
  ok(`I12 no page error at ${w} px, ${th}`, R.errors.length === 0, R.errors.join(' | '));
277
277
  await R.ctx.close();
278
278
  }
@@ -603,13 +603,13 @@ const BMR = { ok: true, state: 'ready', model: { tiles: [
603
603
  await R.ctx.close();
604
604
  }
605
605
 
606
- /* I11 fidelity to the Agreed design (AgInsGoals, AgInsPhone): Nunito, the cozy panel, the pill tabs, the table, the strip and the six tiles */
606
+ /* I11 fidelity to the Agreed design (AgInsGoals, AgInsPhone): Plus Jakarta Sans, the cozy panel, the pill tabs, the table, the strip and the six tiles */
607
607
  for (const [w, h] of [[1440, 900], [390, 844]]) {
608
608
  const R = await open({ w, h }); await sleep(300);
609
609
  const f = await R.page.evaluate(() => { const cs = (s) => getComputedStyle(document.querySelector(s)); const main = cs('main.ins'), tab = cs('#viewChip'), strip = cs('#insHero'), row = document.querySelector('#goalTbl .ins-row').getBoundingClientRect();
610
610
  return { font: cs('body').fontFamily, radius: main.borderTopLeftRadius, bg: main.backgroundColor, tabBg: tab.backgroundColor, tabR: tab.borderTopLeftRadius, tabW: tab.fontWeight, stripR: strip.borderTopLeftRadius, stripBg: strip.backgroundColor, rowH: Math.round(row.height), over: document.documentElement.scrollWidth > innerWidth, fab: document.querySelector('#czFab').getBoundingClientRect().width, fabX: Math.round(document.querySelector('#czFab').getBoundingClientRect().left) }; });
611
611
  const phone = w < 720;
612
- ok(`I11 the page wears the Agreed design at ${w} px: Nunito, the white panel, the pill View chip, the well strip, 56 px floating button`, /^Nunito/.test(f.font) && f.radius === (phone ? '0px' : '28px') && (phone || f.bg === 'rgb(255, 255, 255)') && f.tabBg === 'rgb(244, 243, 240)' && f.tabR === '999px' && +f.tabW >= 700 && (phone || (f.stripR === '16px' && f.stripBg === 'rgb(244, 243, 240)' && f.rowH >= 60)) && !f.over && f.fab === 56 && (phone ? f.fabX > 300 : f.fabX === 36), JSON.stringify(f));
612
+ ok(`I11 the page wears the Agreed design at ${w} px: Plus Jakarta Sans, the white panel, the pill View chip, the well strip, 56 px floating button`, /^"?Plus Jakarta Sans/.test(f.font) && f.radius === (phone ? '0px' : '28px') && (phone || f.bg === 'rgb(255, 255, 255)') && f.tabBg === 'rgb(244, 243, 240)' && f.tabR === '999px' && +f.tabW >= 600 && (phone || (f.stripR === '16px' && f.stripBg === 'rgb(244, 243, 240)' && f.rowH >= 60)) && !f.over && f.fab === 56 && (phone ? f.fabX > 300 : f.fabX === 36), JSON.stringify(f));
613
613
  if (!phone) {
614
614
  await toTrends(R.page);
615
615
  const t = await R.page.evaluate(() => { const tiles = [...document.querySelectorAll('#bmBody [data-tile]')].map((x) => x.getBoundingClientRect()); return { n: tiles.length, tileH: tiles.length ? Math.round(tiles[0].height) : 0, over: document.documentElement.scrollWidth > innerWidth }; });
@@ -193,7 +193,7 @@ const sh = (cwd, args) => spawnSync('git', args, { cwd, encoding: 'utf8' });
193
193
  {
194
194
  // re-pointed again for the Agreed design's Home: the source row is a Needs-you row of Home's list, and the digest is a row of New for you.
195
195
  // Re-pointed for Home A + C (HvAC, HvACDesk): a row is one line, its mark and its title; the subject moved to the row's tooltip and
196
- // accessible name, and the title is 14 px on a desktop, 16 px on a phone, weight 600 (the type scale, feedback batch 15 item 2)
196
+ // accessible name, and the title is the row title role, 15 px at 600 (feedback batch 26 item 6)
197
197
  const ROWS = [['row', '.hm-rm'], ['number', '.hv-d'], ['title', '.hm-rt b']];
198
198
  const DIGS = [['event', ''], ['eventTime', '.ev-tx .ev-mut'], ['eventTitle', '.ev-tx b']];
199
199
  const SANS = 'var(--cz-sans)';
@@ -201,10 +201,10 @@ const sh = (cwd, args) => spawnSync('git', args, { cwd, encoding: 'utf8' });
201
201
  const WANT = {
202
202
  row: { fontFamily: SANS, color: 'var(--cz-ink)', backgroundColor: 'transparent' },
203
203
  number: { backgroundColor: 'var(--cz-blue)', borderRadius: '50%', width: '8px', height: '8px' }, // the row's dot is blue: it waits on you (HvAC .d)
204
- title: { fontFamily: SANS, fontSize: '14px', fontWeight: '600', color: 'var(--cz-ink)' }, // HvACDesk .t; 16px on a phone (HvAC .t b)
204
+ title: { fontFamily: SANS, fontSize: 'var(--fs-row)', fontWeight: 'var(--fw-strong)', color: 'var(--cz-ink)' }, // the row title role (batch 26 item 6), desktop and phone
205
205
  event: { fontFamily: SANS, color: 'var(--cz-ink)', backgroundColor: 'transparent' }, // a row of New for you's table (cozy-ev.css)
206
- eventTime: { fontSize: '12px', color: 'var(--cz-ink3)' },
207
- eventTitle: { fontFamily: SANS, fontSize: '14px', fontWeight: '700', color: 'var(--cz-ink)' },
206
+ eventTime: { fontSize: 'var(--fs-meta)', color: 'var(--cz-ink3)' },
207
+ eventTitle: { fontFamily: SANS, fontSize: 'var(--fs-row)', fontWeight: 'var(--fw-strong)', color: 'var(--cz-ink)' },
208
208
  };
209
209
  const grab = (pg, pairs, base, col) => pg.evaluate(([pairs, want, base, col]) => {
210
210
  const o = {};
@@ -235,7 +235,6 @@ const sh = (cwd, args) => spawnSync('git', args, { cwd, encoding: 'utf8' });
235
235
  await allNeeds(R.page);
236
236
  // on a phone "Not in an initiative" starts folded (HvAC): open it to reach the source row
237
237
  if (w === 390) { const lh = await R.page.$('#hmList .hv-lh[aria-expanded="false"]'); if (lh) await lh.click(); }
238
- WANT.title.fontSize = w === 390 ? '16px' : '14px';
239
238
  await sleep(500);
240
239
  const rowSel = '#hmList .hm-r[data-id^="n:src:"]';
241
240
  const diffs = diff(await grab(R.page, ROWS, rowSel, '.hm-pg'), ROWS, 4); // the row fills its group (the group's own inset aside)
@@ -127,7 +127,7 @@ const vw = () => { const v = document.querySelector('#czViewer:not([hidden])');
127
127
  const selBg = await p.evaluate(() => getComputedStyle(document.querySelector('.hm-r.sel')).backgroundColor);
128
128
  ok('R12 the selected row is tinted (HvACDesk .sel)', selBg && !/rgba\(0, 0, 0, 0\)|transparent/.test(selBg), selBg);
129
129
  const hd = await p.evaluate(() => { const h = document.querySelector('#hmCh'); return h && !h.hidden ? { t: (h.querySelector('.hm-chb') || {}).textContent || '', mark: !!h.querySelector('.hv-mk .hv-d, .hv-mk .hv-ck'), y: Math.round(h.getBoundingClientRect().top), size: getComputedStyle(h.querySelector('.hm-chb')).fontSize } : null; });
130
- ok('R12 the chat\'s header line names the open row: its mark and its title, 16 px (the type scale, batch 15), at the top of the chat card (HvACDesk .ch)', !!hd && hd.t === r0.t && hd.mark && hd.y === 32 && hd.size === '16px', JSON.stringify(hd)); }
130
+ ok('R12 the chat\'s header line names the open row: its mark and its title, 15 px (the row title role, batch 26 item 6), at the top of the chat card (HvACDesk .ch)', !!hd && hd.t === r0.t && hd.mark && hd.y === 32 && hd.size === '15px', JSON.stringify(hd)); }
131
131
 
132
132
  /* R10 */
133
133
  ok('R10 a page where nothing was pressed to record sends no POST', A.posts.filter((x) => /briefing-premium\/(decide|put-off|return|start)/.test(x.path)).length === 0, JSON.stringify(A.posts.map((x) => x.path)));
@@ -273,7 +273,7 @@ const vw = () => { const v = document.querySelector('#czViewer:not([hidden])');
273
273
  const desk = document.querySelector('#hmDesk'), list = document.querySelector('#hmList'), cs = getComputedStyle(document.querySelector('#hmChat')), r = list.getBoundingClientRect();
274
274
  return { inset: Math.round(r.left), radius: cs.borderTopLeftRadius, list: Math.round(list.getBoundingClientRect().width), font: getComputedStyle(document.body).fontFamily, theme: document.documentElement.dataset.theme || '', bg: getComputedStyle(document.body).backgroundColor, rail: !!document.querySelector('nav.rail'), menu: !!document.querySelector('#czMenu, .cz-nav') };
275
275
  });
276
- ok(`R12 Home follows the Home A + C design at 1440 px (HvACDesk), ${theme}: the list at 16 px and 360 px wide, the chat a card with a 24 px radius, Nunito, the floating place menu, no rail`, f.inset === 16 && f.radius === '24px' && f.list === 360 && /Nunito/.test(f.font) && f.menu && !f.rail, JSON.stringify(f));
276
+ ok(`R12 Home follows the Home A + C design at 1440 px (HvACDesk), ${theme}: the list at 16 px and 360 px wide, the chat a card with a 24 px radius, Plus Jakarta Sans, the floating place menu, no rail`, f.inset === 16 && f.radius === '24px' && f.list === 360 && /Plus Jakarta Sans/.test(f.font) && f.menu && !f.rail, JSON.stringify(f));
277
277
  await D.ctx.close();
278
278
  }
279
279
  return finish();
@@ -1,4 +1,30 @@
1
1
  [
2
+ {
3
+ "v": "0.513.0",
4
+ "at": "2026-11-03T15:46:14.696Z",
5
+ "f": 4,
6
+ "prs": [
7
+ {
8
+ "n": 1203,
9
+ "t": "Typography roles with Plus Jakarta Sans, two weights, four sizes",
10
+ "s": "Typography roles with Plus Jakarta Sans, two weights, four sizes",
11
+ "k": "new"
12
+ }
13
+ ]
14
+ },
15
+ {
16
+ "v": "0.512.0",
17
+ "at": "2026-11-03T15:33:10.629Z",
18
+ "f": 4,
19
+ "prs": [
20
+ {
21
+ "n": 1204,
22
+ "t": "Suggest the next prompt and sharpen a rough one in the chat",
23
+ "s": "Suggest the next prompt and sharpen a rough one in the chat",
24
+ "k": "new"
25
+ }
26
+ ]
27
+ },
2
28
  {
3
29
  "v": "0.511.0",
4
30
  "at": "2026-11-03T15:10:29.930Z",
@@ -0,0 +1,40 @@
1
+ {
2
+ "about": "Recorded answers for the next-prompt and sharpen-prompt replay gates (lib/routes-next-prompt.js). Each answer was given by claude-sonnet-5-5 on a real reply from the PMOS project threads on 2026-11-03, except the cases named as made up, which test a drop. The replay never calls a model.",
3
+ "next": [
4
+ { "name": "a reply that asks a question gets the answer as a follow card, citing the line it answers",
5
+ "reply": "#1198 (no initiative without an expected outcome) is merged, and kit-publish is publishing it.\nAll five approved features are now merged except the hindsight engine, which stays unbuilt because there are only 1 of the 10 needed prediction-and-outcome pairs. Do you want to swap it for another idea, such as a period-end KR debrief, or stay at four?",
6
+ "answer": { "cards": [{ "move": "follow", "text": "Yes, swap the hindsight engine for the period-end KR debrief.", "from": "Do you want to swap it for another idea, such as a period-end KR debrief, or stay at four?" }] },
7
+ "expect": { "filed": 1 } },
8
+ { "name": "a retired thing in an initiative chat gets an ask-the-past card, citing the verb that answers it",
9
+ "place": { "initiative": "Fix OOH Digital missing from Board" },
10
+ "reply": "Found the cause: the Board hid initiatives on the Later horizon. Retiring Now/Next/Later everywhere is a bigger change, so I asked for a separate thread for it.",
11
+ "answer": { "cards": [{ "move": "past", "text": "Did we ever decide to retire the Now/Next/Later stage? Answer from the records with file:line sources, or say No record found." }] },
12
+ "expect": { "filed": 1, "cites": ["lib/pmos-verbs.js"] } },
13
+ { "name": "a reply that needs nothing gets no card",
14
+ "reply": "First I'll find why OOH Digital is ticked in the product filter but has no rows on the Board.",
15
+ "answer": { "cards": [] },
16
+ "expect": { "none": true } },
17
+ { "name": "made up: a move PMOS does not have is dropped",
18
+ "reply": "PR #1171 is merged.",
19
+ "answer": { "cards": [{ "move": "deploy", "text": "Deploy PR #1171 to production now." }] },
20
+ "expect": { "none": true } },
21
+ { "name": "made up: a follow card that quotes a line the reply never said is dropped",
22
+ "reply": "PR #1171 is merged. Tell me if any task still sits under \"Not in a stage\".",
23
+ "answer": { "cards": [{ "move": "follow", "text": "Yes, merge it.", "from": "Should I merge PR #1171 now or wait?" }] },
24
+ "expect": { "none": true } },
25
+ { "name": "made up: ask the past outside an initiative chat is dropped",
26
+ "reply": "Retiring Now/Next/Later everywhere is a bigger change.",
27
+ "answer": { "cards": [{ "move": "past", "text": "Why did we retire Now/Next/Later?" }] },
28
+ "expect": { "none": true } }
29
+ ],
30
+ "sharpen": [
31
+ { "name": "a rough date move becomes a rehearsal, citing what the PM typed",
32
+ "text": "move the legal review 3 days later",
33
+ "answer": { "text": "Rehearse moving the legal review task in Ooh-digital 3 days later: show what it pushes back and which dates or tasks shift, then wait for my yes before changing anything." },
34
+ "expect": { "filed": 1 } },
35
+ { "name": "a sharper prompt that says the same is not shown",
36
+ "text": "Hotfix, why OOH digital product isn't shown here",
37
+ "answer": { "text": "Hotfix: why isn't the OOH digital product shown here?" },
38
+ "expect": { "none": true } }
39
+ ]
40
+ }
@@ -9,7 +9,7 @@ timestamp: 2026-11-03
9
9
 
10
10
  Home is the page at `/premium.html`. It follows the Agreed design and the "Uncovered features" canvas (initiative `premium-uncovered-features`). The left side is one list. The middle is the chat. The right side is the Viewer. A row opens its item in the Viewer. On a phone the list is the page, and a row opens its conversation with a back bar.
11
11
 
12
- Home, the Board, Insights and the Viewer use one type scale: 11, 12, 14, 16, 20 and 28 px. A list row's name is 14 px on a desktop and 16 px on a phone, a group head is 16 px, the quiet meta line and a chip are 12 px, a badge is 11 px, a page title is 20 px and a large number is 28 px. The same kind of text has the same size on every page; weight and colour make the difference, not a half pixel.
12
+ Home, the Board, Insights and the Viewer use one typeface, Plus Jakarta Sans, with two weights (regular and semibold) and four sizes: 13, 15, 18 and 26 px. Each kind of text has one role, and the role sets its size, weight and line height: a large number and a page title are 26 px, a panel title is 18 px, a group head, a row's name and a sentence are 15 px, and the quiet meta line, a label and a badge are 13 px. The same kind of text looks the same on every page. Rows have room to breathe, so a list row is about 52 to 58 px high. There is no compact setting. Slides and report templates keep their own sizes.
13
13
 
14
14
  ## The list
15
15
 
@@ -81,6 +81,7 @@ On a desktop the workspace is one frame. [Layout](layout.md) explains each part.
81
81
  - [Initiatives › Timeline](initiatives-timeline.md): when each initiative runs, one row per initiative, and how to reorder rows by their grip.
82
82
  - [Workflows](workflows.md): Settings › Workflows, the stages each kind of initiative goes through, New workflow in the chat, and `pmos workflow`.
83
83
  - [Expected outcomes](outcomes.md): every initiative says what should change once it ships and when to check it. Claude drafts each one from the PRD or the KR, you keep it with ✓, and `pmos outcomes`.
84
+ - [The next prompt, suggested and sharpened](next-prompt.md): after a reply, up to two prompts to send next; while you type a rough prompt, a sharper version in your style (Polish, Prompt engineer, Your own).
84
85
  - [Rehearse a plan](rehearse.md): before you approve a date change or a cut, Claude shows what it breaks or helps in other initiatives, goals and program milestones, with the record each effect comes from, and `pmos rehearse`.
85
86
  - [Forecasts](forecast.md): how a goal, a delivery, a program and a health budget are forecast, the states, the kept baseline, and `pmos forecast`.
86
87
  - [Programs on the Board](programs.md): All products, programs first then products, By product, the program panel and its milestones, and the 60 % side panels.
@@ -0,0 +1,62 @@
1
+ ---
2
+ type: Guide
3
+ title: "The next prompt, suggested and sharpened"
4
+ description: "After a reply, the chat suggests what to say next. While you type a rough prompt, Claude offers a sharper version. What each card means, the styles, the settings, and the guardrails."
5
+ timestamp: 2026-11-03
6
+ ---
7
+
8
+ # The next prompt, suggested and sharpened
9
+
10
+ You do not have to remember phrases such as "grill this". The chat suggests them when they fit.
11
+
12
+ ## After a reply
13
+
14
+ When Claude finishes a reply, up to two cards can show above the box. Usually there is one.
15
+
16
+ - The card Claude recommends is white, with a blue ring and a blue ✦. Another card is grey.
17
+ - Tap a card to put its text in the box. Nothing is sent. Change the text if you want to, then send it with ↑.
18
+ - The cards go away when you type, or when Claude starts a new reply. They do not time out.
19
+ - When there is no clear next step, no card shows.
20
+
21
+ A card suggests only something that works in PMOS today:
22
+
23
+ - an answer to the question or the offer at the end of the reply;
24
+ - "Grill this …" in an initiative's chat ([Claude proposes](claude-proposes.md));
25
+ - "Why did we …?" in an initiative's chat ([Ask about the past](ask-the-past.md));
26
+ - "Rehearse moving …" before a date change or a cut ([Rehearse a plan](rehearse.md));
27
+ - "Run the drift replay" when the weekly check waits for it ([What you might have missed](what-you-missed.md)).
28
+
29
+ Each card names only the initiatives, tasks and PRs that the reply or your message names.
30
+
31
+ ## While you type
32
+
33
+ When you stop typing for about 1.5 seconds on a rough prompt of three or more words, a small spinner card shows while Claude sharpens it. Then one lit **Sharper** card shows. Tap it to put the sharper text in the box. ↶ in the box puts back what you wrote. When the sharper version says the same as yours, no card shows.
34
+
35
+ The sliders icon on the Sharper card sets how Claude sharpens:
36
+
37
+ - **Polish**: clearer and shorter. Your meaning is kept.
38
+ - **Prompt engineer** (the default): the best prompt for the job. It names the real initiative or task and says what a good answer holds. A date move or a cut becomes a rehearsal that waits for your yes.
39
+ - **Your own**: your own wording rules. You write them once in Settings.
40
+
41
+ The same menu has the **Sharpen as I type** switch. Off means no spinner and no Sharper card.
42
+
43
+ ## Settings › Claude › Sharpen prompts
44
+
45
+ | Row | What it does |
46
+ |---|---|
47
+ | Suggest the next prompt | On by default. Off: no cards after a reply. |
48
+ | Sharpen as I type | On when Claude's runs use your API key (Settings › Claude › API key), because a sharper version then shows in about 2 seconds. Without a key, each one is a background Claude run that takes 10 seconds or more, so it starts off. You can turn it on anyway. |
49
+ | Sharpen as | Polish, Prompt engineer or Your own. |
50
+ | Your own rules | The rules for Your own, up to 1000 characters. |
51
+
52
+ ## The guardrails
53
+
54
+ The suggestions and the sharper prompt go through the same guardrail as every AI job in PMOS:
55
+
56
+ - **Propose only.** A card is text you can put in the box. Nothing is sent, and nothing is written to a record, a session or a repository.
57
+ - **Proven only.** Each suggested move points to the line of PMOS code that makes it work, and PMOS checks that line before the card shows. An answer to the reply points to the line of the reply it answers. A card that PMOS cannot check is not shown.
58
+ - **Replay-tested.** A new version shows nothing until its recorded cases pass, and those include cases that must be dropped.
59
+ - **Audited.** Each run writes a line to the AI audit, with no text of your conversation in it.
60
+ - **Off switch.** `pmos ai off next-prompt` stops the suggestions after a reply. `pmos ai off sharpen-prompt` stops the Sharper card. `pmos ai off` stops every AI job.
61
+
62
+ The reply and the words a card refers to are kept in your PMOS state folder (`next-prompt/`, the newest 30 files), outside every repository. Your choices for these rows are kept there too, in `next-prompt/prefs.json`.
@@ -35,7 +35,7 @@ Settings is not a tab. While it is open, no tab is selected. Press a tab to go b
35
35
  | Claude sessions | What a new session starts with: the CLI, the model, the Claude profile and the launch flags for sessions you start, and the flags for automatic sessions. The session limits. Validate consent for this workspace. |
36
36
  | Privacy and data | A list of what leaves this computer and when. Whether a TypeSafe key is saved, the local model that answers when there is no key (Off, Winnow or Laya), and the data folder. |
37
37
  | Appearance | The theme (Same as system, Light or Dark) and the page that opens first. |
38
- | Claude | The Claude profile that new Claude sessions start with, and the Anthropic API key for Claude's own runs on the API (see below). |
38
+ | Claude | The Claude profile that new Claude sessions start with, the Anthropic API key for Claude's own runs on the API, and Sharpen prompts (see below). |
39
39
  | Claude's checks | When Claude grills an initiative by itself (Full, Only at stage exits, or Off) and the monthly credit for its checks ($5, $10, $20 or $50), with what this month used. See below. |
40
40
  | Updates | The version of the app and of each workspace's kit, the check for updates, and what changed in each week's kits (what's new). See below. |
41
41
 
@@ -85,6 +85,8 @@ Under the update card, **Updates** shows what changed in the published kits. **T
85
85
 
86
86
  **API key.** Some runs of Claude, for example Grill, can use the Claude API and not a Claude session. Paste an Anthropic API key and press **Save** to turn this on. The key stays on this computer, in a file only you can read, outside every repository. PMOS never shows the key again: the row only says that a key is set. Press **Remove** to delete it. A key saved here is used before `ANTHROPIC_API_KEY`. A command that a run starts does not get the key. Only the computer that runs the workspace can change it. Set `PMOS_GRILL_API=off` to stop runs on the API.
87
87
 
88
+ **Sharpen prompts.** Suggest the next prompt (on by default) shows up to two prompts above the chat box after a reply. Sharpen as I type offers a sharper version of a rough prompt. It is on when an API key is set, and off without one, because a background run is too slow to follow your typing. Sharpen as picks Polish, Prompt engineer (the default) or Your own, and Your own rules holds the rules for Your own. The same style and switch are in the menu on the chat's Sharper card. See [The next prompt, suggested and sharpened](next-prompt.md).
89
+
88
90
  ## Claude's checks: when Claude grills, and what it may spend
89
91
 
90
92
  Claude grills an initiative by itself and files what it finds under the initiative's ✦ (see the Claude proposes guide). There is no button to start it. This section is the only control.
@@ -88,7 +88,7 @@ const FINDING = { id: 'rf-0123456789', name: 'Partner numbers to Lee', why: 'You
88
88
  };
89
89
  const vClose = async (pg) => { for (let k = 0; k < 4; k++) { const shown = await pg.evaluate(() => { const v = document.querySelector('#czViewer'); return !!v && !v.hidden; }); if (!shown) break; await pg.evaluate(() => window.czViewer && window.czViewer.close()); await sleep(250); } };
90
90
  const sizes = (p, sel) => p.evaluate((s) => [...new Set([...document.querySelectorAll(s + ', ' + s + ' *')].filter((e) => e.childNodes.length && [...e.childNodes].some((n) => n.nodeType === 3 && n.textContent.trim())).map((e) => getComputedStyle(e).fontSize))], sel);
91
- const SCALE = new Set(['11px', '12px', '14px', '16px', '20px', '28px']);
91
+ const SCALE = new Set(['13px', '15px', '18px', '26px']); // the typography roles (batch 26 item 6)
92
92
  const panel = (p) => p.evaluate(() => {
93
93
  const b = document.querySelector('#czViewer:not([hidden]) .ins-rpd') || document.querySelector('#side:not([hidden]) .ins-rpd'); if (!b) return null;
94
94
  return { title: (document.querySelector('#czViewer .cz-vw-title') || {}).textContent || '', kind: (document.querySelector('#czViewer .cz-vw-kind') || {}).textContent || '', sum: (b.querySelector('.ins-rps b') || {}).textContent || '',
@@ -157,7 +157,7 @@ const WEEKLY = { view: 'view', weeks: 3, day: 'Mon', opens: 4 };
157
157
  await p.evaluate(() => document.querySelector('#mdHost').remove());
158
158
 
159
159
  /* ── T: the type scale ── */
160
- const SCALE = new Set(['11px', '12px', '14px', '16px', '20px', '28px']);
160
+ const SCALE = new Set(['13px', '15px', '18px', '26px']); // the typography roles (batch 26 item 6)
161
161
  const sizes = await p.evaluate(() => [...new Set([...document.querySelectorAll('#insHero, #insHero *, #nsRow, #nsRow *')].filter((e) => e.childNodes.length && [...e.childNodes].some((n) => n.nodeType === 3 && n.textContent.trim())).map((e) => getComputedStyle(e).fontSize))]);
162
162
  ok('T the strip and the drafts use the type scale only', sizes.every((s) => SCALE.has(s)), sizes.join());
163
163
  ok('no page errors', !A.errors.length, A.errors.join(' | '));