create-pmos 0.185.0 → 0.187.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 (41) 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/scripts/workflow-state.js +2 -1
  6. package/payload/state/discoveries.md +1 -1
  7. package/workspace/c3-ib-triage-backlog-test.js +20 -13
  8. package/workspace/c3-ini-life-test.js +167 -0
  9. package/workspace/c3-initiatives-test.js +6 -6
  10. package/workspace/c3-phone-test.js +67 -16
  11. package/workspace/c3-quiet-test.js +2 -1
  12. package/workspace/c3-report-setup-test.js +1 -0
  13. package/workspace/guide/initiative-boards.md +37 -2
  14. package/workspace/guide/phone-companion.md +19 -4
  15. package/workspace/guide/reports-set-up.md +4 -2
  16. package/workspace/lib/engine/boards.js +73 -18
  17. package/workspace/lib/engine/workflow-state.js +2 -1
  18. package/workspace/lib/initiative-rows.js +2 -2
  19. package/workspace/lib/routes-boards.js +119 -6
  20. package/workspace/lib/routes-meeting-focus.js +19 -7
  21. package/workspace/lib/routes-report-setups.js +37 -4
  22. package/workspace/lib/vendor/scripts/workflow-state.js +2 -1
  23. package/workspace/package-lock.json +2 -2
  24. package/workspace/package.json +1 -1
  25. package/workspace/public/pm/c3-demo-boards.js +82 -17
  26. package/workspace/public/pm/c3-demo-focus.js +39 -1
  27. package/workspace/public/pm/c3-demo-rs.js +3 -1
  28. package/workspace/public/pm/c3-focus.css +53 -0
  29. package/workspace/public/pm/c3-focus.js +188 -44
  30. package/workspace/public/pm/c3-ib-initiatives.js +127 -60
  31. package/workspace/public/pm/c3-ib-items.js +2 -2
  32. package/workspace/public/pm/c3-ini-life.css +32 -0
  33. package/workspace/public/pm/c3-ini-life.js +379 -0
  34. package/workspace/public/pm/c3-ini-ui.js +12 -7
  35. package/workspace/public/pm/c3-initiatives.js +14 -4
  36. package/workspace/public/pm/c3-phone.css +3 -1
  37. package/workspace/public/pm/c3-phone.js +28 -8
  38. package/workspace/public/pm/c3-report-setup.js +8 -3
  39. package/workspace/public/pm/c3-report-store.js +14 -1
  40. package/workspace/server.js +7 -2
  41. package/workspace/test-lists/c3-ini-life.json +9 -0
@@ -14,7 +14,7 @@ import { openSheet, closeSheet } from './c3-sheet.js';
14
14
  import { openDocSheet } from './c3-doc-sheet.js';
15
15
  import { toast } from './c3-toast.js';
16
16
  import { FORMATS, OUTPUTS, formatOf, ensureReportCss } from './c3-doc-formats.js';
17
- import { RS, onReports, loadReports, askPmos, changeIt, cancelAsk, setupFrom, dismissLater, pauseResume, fromDashboard, goTo, evidenceKind, normProposal, scheduleLabel, nextDraftDate, dayText, docsOf, outChip, copyChip, srcChip } from './c3-report-store.js';
17
+ 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';
18
18
 
19
19
  const MON = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
20
20
  /** 'Wed 21 Oct 2026' for a day, 'Oct 2026' for a month, any other slot as it is. */
@@ -122,6 +122,7 @@ function askCard(a, redo) {
122
122
  }
123
123
 
124
124
  /* ── a report document in the document sheet ── */
125
+ RS.openDoc = (d) => openReportDoc(d); // the store's toasts open a document through this
125
126
  export function openReportDoc(d, mode) {
126
127
  ensureReportCss();
127
128
  const setup = RS.setups.find((x) => x.id === d.setup) || null; const cyc = slotText(d.slot);
@@ -205,7 +206,10 @@ export function mountReportSetup(host, ctx) {
205
206
  el('span', { class: 'chips2' }, stag(d), copyChip(d.output, d.copy), pbtn('ghost', 'Open', 'out', { onclick: () => openReportDoc(d) })))));
206
207
  function openSetup(s) {
207
208
  const L = docsOf(RS.docs, s.id); const d = L.find((x) => x.state === 'draft') || L[0];
208
- if (d) openReportDoc(d); else toast('No draft yet. The first draft is ' + (s.next_draft ? dayText(s.next_draft) : 'when you confirm the day') + '.');
209
+ if (d) { openReportDoc(d); return; }
210
+ if (s.source !== 'work' || s.status !== 'active') { toast('No draft yet. The first draft is ' + (s.next_draft ? dayText(s.next_draft) : 'when you confirm the day') + '.'); return; }
211
+ toast('Drafting ' + s.name + ' now. Nothing is sent.');
212
+ draftNow(s).then((nd) => { if (nd) openReportDoc(nd); });
209
213
  }
210
214
  function setupRow(s) {
211
215
  const paused = s.status === 'paused'; const cy = docsOf(RS.docs, s.id); const n = s.cycles != null ? s.cycles : cy.length; const open = RS.cyc.has(s.id);
@@ -215,7 +219,8 @@ export function mountReportSetup(host, ctx) {
215
219
  chip(icon('cal', 'sm'), s.label || scheduleLabel(s.schedule)), paused ? null : chip(icon('clock', 'sm'), 'Next draft ' + (s.next_draft ? dayText(s.next_draft) : 'not set yet')),
216
220
  chip(icon('mail', 'sm'), 'Last issued ' + (s.last_issued ? dayText(s.last_issued) : 'not yet'))),
217
221
  open ? cycRows(s) : null),
218
- el('div', { class: 'acts' }, pbtn('outline', 'Open', 'out', { 'data-rs-act': 'open-reg', onclick: () => openSetup(s) }),
222
+ el('div', { class: 'acts' }, cy.length || paused || s.source !== 'work' ? pbtn('outline', 'Open', 'out', { 'data-rs-act': 'open-reg', onclick: () => openSetup(s) })
223
+ : pbtn('outline', 'Draft it now', 'spark', { 'data-rs-act': 'draft-now', onclick: () => openSetup(s) }),
219
224
  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(); } }),
220
225
  pbtn('ghost', paused ? 'Resume' : 'Pause', paused ? 'play' : 'pause', { 'data-rs-act': 'pause', onclick: () => pauseResume(s, !paused) })));
221
226
  }
@@ -114,9 +114,22 @@ export async function setupFrom(body) {
114
114
  if (r.setup && r.setup.id) RS.newIds.add(r.setup.id);
115
115
  await loadReports(RS.ws, true);
116
116
  const fd = r.first_draft; const first = (r.setup && r.setup.next_draft) || (fd && typeof fd === 'object' ? fd.day || fd.slot : fd);
117
- toast('Set up. First draft ' + (first ? dayText(first) : 'once you confirm the day') + '. Nothing is sent.');
117
+ if (r.drafted && r.drafted.path) openDraftToast(r.setup.id, 'Set up. The first draft is ready to read. Nothing is sent.');
118
+ else toast('Set up. First draft ' + (first ? dayText(first) : 'once you confirm the day') + '. Nothing is sent.' + (r.drafted && r.drafted.error ? ' It could not be drafted yet: ' + r.drafted.error + '.' : ''));
118
119
  return r;
119
120
  }
121
+ /* the page that draws documents sets RS.openDoc, so a toast here can open one without this store importing the page */
122
+ function openDraftToast(id, text) {
123
+ const d = (RS.docs || []).filter((x) => x.setup === id && x.state === 'draft').sort((a, b) => (a.slot < b.slot ? 1 : -1))[0];
124
+ if (d && typeof RS.openDoc === 'function') toast({ text, actionLabel: 'Open', timeout: 8000, onAction: () => RS.openDoc(d) });
125
+ else toast(text);
126
+ }
127
+ /** Draft it now: the next cycle of a set-up report. → the new draft document, or null. */
128
+ export async function draftNow(setup) {
129
+ const r = await post('draft', { id: setup.id }); if (!r || !r.ok) { fail(r, 'It was not drafted'); return null; }
130
+ await loadReports(RS.ws, true);
131
+ return (RS.docs || []).find((x) => x.setup === setup.id && x.slot === r.drafted.slot) || null;
132
+ }
120
133
  /** Not a report: the finding leaves the list at once, the POST waits five seconds, and Undo cancels it. */
121
134
  export function dismissLater(id, onChange) {
122
135
  RS.hidden.add(id); emit(); if (onChange) onChange();
@@ -1969,7 +1969,8 @@ function readLifecycle(w) {
1969
1969
  for (const r of readLedger(w, 'lifecycle.jsonl')) {
1970
1970
  if (!r || typeof r.initiative_id !== 'string') continue;
1971
1971
  if (r.state !== 'active' && !STOPPED_STATES.includes(r.state)) continue;
1972
- latest.set(r.initiative_id, { state: r.state, reason: r.reason || '', pm: r.pm || '', at: r.at || '' });
1972
+ // `back` (c3-initiative-lifecycle): the day an on-hold initiative comes back to the PM, when the hold named one.
1973
+ latest.set(r.initiative_id, { state: r.state, reason: r.reason || '', pm: r.pm || '', at: r.at || '', ...(r.state === 'on-hold' && /^\d{4}-\d{2}-\d{2}$/.test(r.back || '') ? { back: r.back } : {}) });
1973
1974
  }
1974
1975
  return latest;
1975
1976
  }
@@ -8648,6 +8649,10 @@ async function handleRequest(req, res) {
8648
8649
  if (body.state !== 'active' && !STOPPED_STATES.includes(body.state)) {
8649
8650
  return json(res, { ok: false, error: `state must be one of ${STOPPED_STATES.concat('active').join(', ')}` }, 400);
8650
8651
  }
8652
+ // A hold may name the day it comes back to the PM (c3-initiative-lifecycle). Only on-hold carries it.
8653
+ if (body.back != null && (body.state !== 'on-hold' || !/^\d{4}-\d{2}-\d{2}$/.test(String(body.back)))) {
8654
+ return json(res, { ok: false, error: 'back is a day (YYYY-MM-DD) and only goes with on-hold' }, 400);
8655
+ }
8651
8656
  const w = getWs(body.ws);
8652
8657
  if (!w) return json(res, { ok: false, error: 'no workspace' }, 400);
8653
8658
  const known = new Set(initiativeIndex(w.root).initiatives.map((x) => x.id));
@@ -8662,7 +8667,7 @@ async function handleRequest(req, res) {
8662
8667
  grant = m.grant_id;
8663
8668
  }
8664
8669
  const at = nowISO();
8665
- const recs = ids.map((x) => ({ initiative_id: x, state: body.state, reason, pm: 'wawan', by: g.actor.by, via: g.actor.via, at, ...(grant ? { grant } : {}) }));
8670
+ const recs = ids.map((x) => ({ initiative_id: x, state: body.state, reason, ...(body.back ? { back: String(body.back) } : {}), pm: 'wawan', by: g.actor.by, via: g.actor.via, at, ...(grant ? { grant } : {}) }));
8666
8671
  const rec = recs[0];
8667
8672
  // The ledger the ENGINE reads for this repository's layout (scripts/workflow-state.js#layout): a repository
8668
8673
  // with planning/initiatives/ keeps its ledgers in planning/runs/, a kit in pmos/state/. Writing anywhere else
@@ -0,0 +1,9 @@
1
+ {
2
+ "_comment": "c3-initiative-lifecycle: On hold, Cancelled, merge and archive on the console3 Initiatives page, and the two-row board header, in demo mode (browser).",
3
+ "panels": [
4
+ "c3-ini-life-test.js"
5
+ ],
6
+ "seconds": {
7
+ "c3-ini-life-test.js": 30
8
+ }
9
+ }