create-pmos 0.240.0 → 0.241.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 (37) hide show
  1. package/package.json +1 -1
  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/c3-report-structure-real-test.js +211 -0
  7. package/workspace/c3-report-structure-test.js +382 -0
  8. package/workspace/guide/index.md +2 -0
  9. package/workspace/guide/report-design.md +4 -0
  10. package/workspace/guide/report-structure.md +91 -0
  11. package/workspace/guide/reports-dashboards.md +1 -1
  12. package/workspace/guide/reports-set-up.md +2 -0
  13. package/workspace/lib/charts-store.js +124 -0
  14. package/workspace/lib/headless.js +17 -3
  15. package/workspace/lib/needs-providers.js +1 -0
  16. package/workspace/lib/report-structure-needs.js +64 -0
  17. package/workspace/lib/report-structure.js +515 -0
  18. package/workspace/lib/route-modules.js +1 -0
  19. package/workspace/lib/routes-report-setups.js +11 -6
  20. package/workspace/lib/routes-report-structure.js +619 -0
  21. package/workspace/package-lock.json +2 -2
  22. package/workspace/package.json +1 -1
  23. package/workspace/public/pm/c3-demo-rd.js +4 -0
  24. package/workspace/public/pm/c3-design-studio.js +7 -3
  25. package/workspace/public/pm/c3-report-setup.js +3 -0
  26. package/workspace/public/pm/c3-report-structure.css +40 -0
  27. package/workspace/public/pm/c3-report-structure.js +455 -0
  28. package/workspace/public/pm/c3-reports.css +2 -0
  29. package/workspace/public/pm/c3-reports.js +22 -3
  30. package/workspace/public/pm/c3-shell.js +2 -0
  31. package/workspace/server.js +38 -9
  32. package/workspace/skills/report-source-check.skill +45 -0
  33. package/workspace/skills/report-structure-dry.skill +51 -0
  34. package/workspace/skills/report-structure.skill +61 -0
  35. package/workspace/skills/report-template.skill +10 -0
  36. package/workspace/test-lists/report-structure-backend.json +5 -0
  37. package/workspace/test-lists/report-structure-ui.json +11 -0
@@ -407,6 +407,10 @@ function press(k, b) {
407
407
  /** The routes. Returns a Response, or undefined for a route this file does not answer. */
408
408
  export function rdRoute(method, u, body) {
409
409
  const p = u.pathname; const qp = u.searchParams;
410
+ /* the Structure step (initiative report-structure): demo mode holds no structure and no data chart, and saves nothing */
411
+ if (p === '/api/report-structure' && method === 'GET') { const id = qp.get('id'); const s = ctx.setups().find((x) => x.id === id); return s ? json({ ok: true, setup: { id, name: s.name, cadence: s.schedule && s.schedule.cadence }, structure: { running: false, job: null, current: null, saved: null, versions: [], log: [], plan: null, dry: null, checks: {}, errors: [] } }) : json({ ok: false, error: 'no such report' }, 404); }
412
+ if (p === '/api/charts' && method === 'GET') return json({ ok: true, charts: [] });
413
+ if (p.startsWith('/api/report-structure') && method === 'POST') return json({ ok: false, error: 'Demo data: nothing is saved' });
410
414
  if (!p.startsWith('/api/report-design') && !p.startsWith('/api/report-notes')) return undefined;
411
415
  if (method === 'GET') {
412
416
  if (p === '/api/report-design') { const id = qp.get('id'); if (!ctx.setups().some((x) => x.id === id)) return json({ ok: false, error: 'no such report' }, 404); return json(designView(id)); }
@@ -16,6 +16,7 @@ import { toast } from './c3-toast.js';
16
16
  import { RS, scheduleLabel } from './c3-report-store.js';
17
17
  import { formatOf } from './c3-doc-formats.js';
18
18
  import { RD, failed, splitRender, slideCard, frame, wireFrame, diffOutlines, ensureDeckCss, slotWord } from './c3-report-deck.js';
19
+ import { openStructure, structureSection } from './c3-report-structure.js';
19
20
 
20
21
  const MAX = 20 * 1024 * 1024;
21
22
  const KIND_OF = (name) => { const e = String(name || '').toLowerCase().split('.').pop(); return e === 'pdf' ? 'pdf' : e === 'pptx' ? 'pptx' : e === 'docx' ? 'docx' : /^(png|jpe?g)$/.test(e) ? 'image' : ''; };
@@ -304,7 +305,9 @@ export function openStudio({ ws, id, name, view, onClose } = {}) {
304
305
  const sb = body.closest('.sb'); const y = sb ? sb.scrollTop : 0; const log = body.querySelector('.rd-dks-log'); const ly = log ? log.scrollTop : null;
305
306
  const focus = document.activeElement && body.contains(document.activeElement) && document.activeElement.matches('[data-rd-in],[data-rd-link]') ? document.activeElement.getAttribute('data-rd-in') != null ? '[data-rd-in]' : '[data-rd-link]' : null;
306
307
  S.dks = S.dks || el('div', { class: 'rd-dks' }); S.out = S.out || el('div', { class: 'rd-dks-out', 'data-rd-preview': '' });
307
- keep(body, el('div', { class: 'rd-dk-bar' }, el('div', { class: 'chips2 rd-wrap' }, el('div', { class: 'seg', role: 'group', 'aria-label': 'Report type' },
308
+ keep(body, el('div', { class: 'rd-dk-bar' }, el('div', { class: 'chips2 rd-wrap' }, el('div', { class: 'seg', role: 'group', 'aria-label': 'Studio step', 'data-rd-steps': '' },
309
+ btn({ 'data-rd-step': 'structure', 'aria-pressed': 'false', onclick: () => openStructure({ ws, id, name: nm }) }, icon('todo', 'sm'), '1 Structure'), btn({ 'data-rd-step': 'design', 'aria-pressed': 'true' }, icon('img', 'sm'), '2 Design')),
310
+ el('div', { class: 'seg', role: 'group', 'aria-label': 'Report type' },
308
311
  btn({ 'data-rd-fmt': 'deck', 'aria-pressed': String(K.fmt === 'deck'), onclick: () => setFmt('deck') }, icon('img', 'sm'), 'Deck'), btn({ 'data-rd-fmt': 'doc', 'aria-pressed': String(K.fmt === 'doc'), onclick: () => setFmt('doc') }, icon('doc', 'sm'), 'Document')),
309
312
  el('b', null, nm), D.saved ? el('span', { class: 'chip2 ds-good', 'data-rd-saved': String(D.saved.version) }, icon('check', 'sm'), 'Saved design: D' + D.saved.version) : el('span', { class: 'chip2', 'data-rd-saved': '' }, 'Not saved yet')),
310
313
  el('div', { class: 'chips2' }, pbtn('primary', 'This is it', 'check', { 'data-rd-act': 'accept', disabled: !D.versions.length, onclick: () => accept() }),
@@ -332,8 +335,9 @@ export function openBrief({ ws, id, name } = {}) {
332
335
  const nm = setup.name || name || id; const out = formatOf(setup.output);
333
336
  const lh = el('div', { class: 'lh' }, el('span', { class: 'ki' }, icon('report')), el('div', { class: 'rs-min0' }, el('b', null, nm),
334
337
  el('div', { class: 'dim' }, (setup.label || scheduleLabel(setup.schedule)) + '. Output: ' + (setup.output === 'google-slides' ? 'a slide deck in PMOS. A Google Slides copy is optional.' : out.l + '.'))));
338
+ const SS = await structureSection({ ws, id, name: nm }); // the saved structure (step 1), above the design
335
339
  if (!D.saved) {
336
- put(body, lh, sect({ ic: 'spark', title: 'Design', attrs: { 'data-rd-design': '' } }, el('div', { class: 'fold', 'data-rd-nosaved': '' }, icon('info', 'sm'), ' No saved design yet. Describe the report and refine it in the Design studio, then press This is it.'),
340
+ put(body, lh, SS, sect({ ic: 'spark', title: 'Design', attrs: { 'data-rd-design': '' } }, el('div', { class: 'fold', 'data-rd-nosaved': '' }, icon('info', 'sm'), ' No saved design yet. Describe the report and refine it in the Design studio, then press This is it.'),
337
341
  el('div', { class: 'ds-acts' }, pbtn('primary', 'Design how it looks', 'spark', { 'data-rd-act': 'studio', onclick: () => openStudio({ ws, id, name: nm }) }))));
338
342
  return;
339
343
  }
@@ -345,7 +349,7 @@ export function openBrief({ ws, id, name } = {}) {
345
349
  const rules = D.rules || {};
346
350
  const cycHost = el('ul', { class: 'chgl', 'data-rd-briefcycles': '' });
347
351
  const docs = (RS.docs || []).filter((d) => d.setup === id).sort((a, b) => String(b.slot).localeCompare(String(a.slot)));
348
- put(body, lh,
352
+ put(body, lh, SS,
349
353
  sect({ ic: 'spark', title: 'Design', right: el('span', { class: 'tag acc' }, 'Design D' + n), attrs: { 'data-rd-design': '' } },
350
354
  el('div', { class: 'dim rd-small' }, 'Written by PMOS from your studio session. It is not a template: PMOS reads it each cycle and drafts to match it. Saved ' + dayAt(D.saved.at) + ', version D' + n + '.'),
351
355
  el('h3', { class: 'ds-h' }, 'Your description'), el('blockquote', { class: 'rd-dks-q', 'data-rd-desc': '' }, D.description || 'No description.'),
@@ -16,6 +16,7 @@ import { toast } from './c3-toast.js';
16
16
  import { FORMATS, OUTPUTS, formatOf, ensureReportCss } from './c3-doc-formats.js';
17
17
  import { createDeck } from './c3-report-deck.js';
18
18
  import { openStudio, openBrief } from './c3-design-studio.js';
19
+ import { openStructure } from './c3-report-structure.js';
19
20
  import { RS, onReports, loadReports, askPmos, changeIt, cancelAsk, setupFrom, draftNow, dismissLater, pauseResume, fromDashboard, goTo, evidenceKind, normProposal, scheduleLabel, nextDraftDate, dayText, docsOf, outChip, copyChip, srcChip } from './c3-report-store.js';
20
21
 
21
22
  const MON = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
@@ -118,6 +119,7 @@ function askCard(a, redo) {
118
119
  return sect({ ic: 'spark', title: 'PMOS answer', attrs, right: confTag('') }, el('div', { class: 'rs-stack' }, head,
119
120
  lbl('WHAT PMOS FOUND'), ...foundList(P), lbl('PROPOSED REPORT'), proposed(key, P, redo), lbl('LEARNED FROM'), ...[].concat(learnedBlock(P)), lbl('FIXES IT WILL APPLY'), fixesList(P), ...(cb ? [].concat(cb) : []),
120
121
  el('div', { class: 'chips2' }, pbtn('primary', 'Set up', 'check', { 'data-rs-act': 'setup', onclick: () => setupFrom({ ask: a.id, proposal: P }) }),
122
+ pbtn('outline', 'Shape it with PMOS', 'todo', { 'data-rs-act': 'shape', title: 'Set the report up, then decide with PMOS what it answers, where each number comes from and how it is worked out', onclick: async () => { const r = await setupFrom({ ask: a.id, proposal: P }); if (r && r.ok && r.setup) openStructure({ ws: RS.ws, id: r.setup.id, name: r.setup.name }); } }),
121
123
  pbtn('outline', 'Change', 'edit', { 'data-rs-act': 'change', onclick: () => { RS.chg[key] = { ...st, open: true }; redo(); } }),
122
124
  pbtn('ghost', 'Cancel', 'x', { 'data-rs-act': 'cancel-ask', onclick: async () => { const r = await cancelAsk(a.id); if (r && r.ok) toast('Cancelled. Nothing was set up.'); } })),
123
125
  help('PMOS reads your past copies to learn the sections. It drafts only; you issue.')));
@@ -232,6 +234,7 @@ export function mountReportSetup(host, ctx) {
232
234
  open ? cycRows(s) : null),
233
235
  el('div', { class: 'acts' }, cy.length || paused || s.source !== 'work' ? pbtn('outline', 'Open', 'out', { 'data-rs-act': 'open-reg', onclick: () => openSetup(s) })
234
236
  : pbtn('outline', 'Draft it now', 'spark', { 'data-rs-act': 'draft-now', onclick: () => openSetup(s) }),
237
+ s.source === 'work' ? pbtn('ghost', 'Shape the report', 'todo', { 'data-rs-act': 'shape', onclick: () => openStructure({ ws: RS.ws, id: s.id, name: s.name }) }) : null,
235
238
  s.source === 'work' ? pbtn('ghost', 'Design how it looks', 'spark', { 'data-rs-act': 'design', onclick: () => openStudio({ ws: RS.ws, id: s.id, name: s.name }) }) : null,
236
239
  pbtn('ghost', 'All cycles (' + n + ')', 'fold', { 'data-rs-act': 'cyc', 'aria-expanded': String(open), onclick: () => { if (RS.cyc.has(s.id)) RS.cyc.delete(s.id); else RS.cyc.add(s.id); drawSetups(); } }),
237
240
  pbtn('ghost', paused ? 'Resume' : 'Pause', paused ? 'play' : 'pause', { 'data-rs-act': 'pause', onclick: () => pauseResume(s, !paused) })));
@@ -0,0 +1,40 @@
1
+ /* pm/c3-report-structure.css — the Structure step of the report studio (c3-report-structure.js; mockup 43-reportstructure.css).
2
+ The conversation reuses the Design studio's .rd-dks-* rules (c3-report-deck.css). Tokens only; font sizes are the --fs-* scale. */
3
+ .c3 .rs-st { display: grid; gap: var(--sp-3); min-width: 0; }
4
+ .c3 .rs-st-bar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: space-between; }
5
+ .c3 .rs-st-bar .seg button { display: inline-flex; gap: 4px; align-items: center; }
6
+ .c3 .rs-st .rd-dks-out > .card.sect { margin: 0; }
7
+ .c3 .rs-st-q { display: grid; gap: 4px; }
8
+ .c3 .rs-st-q b { font-size: var(--fs-2); font-weight: 500; }
9
+ .c3 .rs-st-small { font-size: var(--fs-2); }
10
+ .c3 .rs-st-tiny { font-size: var(--fs-1); }
11
+ .c3 .rs-st-fg { color: var(--foreground); }
12
+ .c3 .rs-st-field { display: grid; gap: 2px; font-size: var(--fs-2); }
13
+ .c3 .rs-st-field input { width: 100%; box-sizing: border-box; padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px; background: var(--card); color: var(--foreground); font: var(--fs-3)/1.4 var(--sans); }
14
+ .c3 .rs-st-form { display: grid; gap: 8px; padding: 8px 10px; margin: 0 0 4px; border: 1px solid var(--border); border-radius: 8px; background: var(--background); }
15
+ .c3 .rs-st-tw { overflow-x: auto; }
16
+ .c3 .rs-st-tbl { min-width: 640px; font-size: var(--fs-2); }
17
+ .c3 .rs-st-tbl td { vertical-align: top; }
18
+ .c3 .rs-st-row.on td { background: var(--muted); }
19
+ .c3 .rs-st-tbl .tt { text-align: left; }
20
+ .c3 .rs-st-det > td { border-top: 0; padding-top: 0; }
21
+ .c3 .rs-st-detb { display: grid; gap: 10px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--background); }
22
+ .c3 .rs-st-detb p, .c3 .rs-st-opt p, .c3 .rs-st-p { margin: 2px 0 0; font-size: var(--fs-3); line-height: 1.5; }
23
+ .c3 .rs-st-detb .dim.mono, .c3 .rs-st-opt .dim.mono { font-size: var(--fs-1); }
24
+ .c3 .rs-st-code { font-size: var(--fs-2); overflow-wrap: anywhere; }
25
+ .c3 .rs-st-ol { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
26
+ .c3 .rs-st-ol > li { display: grid; grid-template-columns: 24px minmax(0, 1fr); gap: 8px; align-items: start; }
27
+ .c3 .rs-st-ol .chips { margin-top: 4px; }
28
+ .c3 .rs-st-n { display: grid; place-items: center; width: 24px; height: 24px; border: 1px solid var(--border); border-radius: 6px; font-size: var(--fs-1); color: var(--muted-foreground); }
29
+ .c3 .rs-st-mini { margin: 0; padding-left: 20px; display: grid; gap: 2px; font-size: var(--fs-2); }
30
+ .c3 .rs-st-opts { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--sp-2); }
31
+ .c3 .rs-st-opt { display: grid; gap: 10px; align-content: start; margin: 0; }
32
+ .c3 .rs-st-chk > li { grid-template-columns: 16px minmax(0, 1fr) auto; }
33
+ .c3 .rs-st-saved { display: grid; gap: var(--sp-3); }
34
+ .c3 .rs-st .rd-dks-panel { display: flex; flex-direction: column; max-height: 42vh; overflow: auto; }
35
+ .c3 .rs-st .rd-dks-panel > * { flex: none; }
36
+ @media (max-width: 760px) {
37
+ .c3 .rs-st .rd-dks { grid-template-columns: minmax(0, 1fr); }
38
+ .c3 .rs-st .rd-dks-chat { position: static; max-height: none; }
39
+ .c3 .rs-st .rd-dks-log { max-height: 420px; }
40
+ }