create-pmos 0.208.0 → 0.209.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-pmos",
3
- "version": "0.208.0",
3
+ "version": "0.209.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": {
@@ -1,3 +1,3 @@
1
- kernel-source: pmos@709d321
1
+ kernel-source: pmos@dfd2453
2
2
  assembled: 2026-10-14
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-10-14T01:38:03Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-14T01:49: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-14T01:38:03Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-14T01:49: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-14T01:38:03Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-14T01:49:09Z"}
6
6
  status: stable
7
7
  ---
8
8
 
@@ -98,6 +98,11 @@ const writes = (p) => (window.__c3demoWrites || []).filter((w) => w.path === p |
98
98
  }
99
99
  /* ── the brief: everything the desktop Briefing counts waits here, and each row opens ── */
100
100
  ok('brief: the morning card counts what waits on you', await waitFor(page, () => /wait on you|waits on you/.test(document.querySelector('[data-c3ph-moment="morning"]')?.textContent || ''), null, 8000));
101
+ ok('brief: the morning shows the top three points of the Briefing deck', await waitFor(page, () => { const c = document.querySelector('[data-c3ph-moment="morning"]'); return !!c && c.querySelectorAll('[data-c3ph-point]').length === 3 && /things this morning/.test(c.textContent); }, null, 8000));
102
+ await page.click('[data-c3ph-moment="morning"] [data-c3ph-point]');
103
+ ok('brief: a point opens its details', await waitFor(page, () => !!document.querySelector('[data-shell="sheet"] [data-c3ph-need]'), null, 5000));
104
+ await page.keyboard.press('Escape');
105
+ await waitFor(page, () => !document.querySelector('[data-shell="sheet"] [data-c3ph-need]'), null, 3000);
101
106
  ok('brief: the briefing needs are in the waiting list', await waitFor(page, () => !!document.querySelector('[data-c3ph-all-open]'), null, 8000));
102
107
  if (await page.$('[data-c3ph-all-open]')) {
103
108
  await page.click('[data-c3ph-all-open]');
@@ -15,7 +15,7 @@ The top card shows where you are in your day and why it is useful now. PMOS choo
15
15
 
16
16
  | Moment | When | What the card shows |
17
17
  |---|---|---|
18
- | Your day | Before noon, with no meeting in the next 30 minutes | The next meeting, the number of things that wait on you and how many are overdue, and **The brief of this meeting** |
18
+ | Your morning | Before noon, with no meeting in the next 30 minutes | Your briefing: the top three points of the desktop Briefing, each opens its details. Then how many things wait on you, how many drafts wait for your review, and the first meeting with **The brief of this meeting** |
19
19
  | Before you walk in | The next meeting starts in 30 minutes or less | What is prepared for it, the items to close first, each with **Done**, and **The brief of this meeting** |
20
20
  | In the meeting | A meeting is in progress | **Record this meeting?** with **Record** and **Not this one**. Recording opens focus (see below). |
21
21
  | Just ended | A meeting ended less than 20 minutes ago | **What came out of it?** If you recorded the meeting, the card shows the summary, the decisions, the actions and the open questions from its notes, the same notes as on the Notes page. While PMOS writes the notes, the card says so. If you did not record it, **Tell PMOS** puts the meeting name in the bar so you can type the result. |
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pmos-workspace",
3
- "version": "0.208.0",
3
+ "version": "0.209.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pmos-workspace",
9
- "version": "0.208.0",
9
+ "version": "0.209.0",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@xterm/addon-fit": "^0.11.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmos-workspace",
3
- "version": "0.208.0",
3
+ "version": "0.209.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",
@@ -58,3 +58,4 @@
58
58
  .c3 .c3ph-stats div{display:grid;gap:2px}
59
59
  .c3 .c3ph-stats b{font-size:20px;font-variant-numeric:tabular-nums}
60
60
  .c3 .c3ph-stats span{font-size:11px;color:var(--muted-foreground);line-height:1.25}
61
+ .c3 .c3ph-pts{display:grid;gap:2px}
@@ -8,7 +8,8 @@
8
8
  * actions and questions picked out so far (dest-notes.js partsOf), as the desktop note shows them
9
9
  * before a meeting: when it starts, who, what is prepared, the "before you go" items with Done
10
10
  * just ended: tell PMOS what came out of it a free gap: the own items that fit, with Done and Later today
11
- * morning: the next meeting and how much waits
11
+ * morning ("Your morning"): the briefing, the top three cards of the desktop Briefing's deck (GET /api/briefing cards), each
12
+ * opening its details; how much waits; the drafts waiting for review; then the first meeting with its brief
12
13
  * working hours, and a free gap: "Finished for you", the agent's drafts of today (GET /api/phone-day finished), each opens
13
14
  * to read (c3-phone-detail.js openDraft); approving one waits for the desk
14
15
  * evening (recap, from 17:00): how today went (GET /api/phone-day recap), what moved, what is still open, one thing tonight
@@ -31,7 +32,7 @@
31
32
  * There is no way to the desktop pages on a phone.
32
33
  * The moment is worked out again every minute; when it changes, a short banner says so. ?at=HH:MM sets the clock in demo mode only. */
33
34
  import { el } from './dom.js';
34
- import { button, iconButton } from './primitives.js';
35
+ import { button, iconButton, ensureComponentsCss } from './primitives.js';
35
36
  import { pmPost, pmGet } from './http.js';
36
37
  import { ensureC3Css, c3ic } from './pm-work-view.js';
37
38
  import { onBriefPage, loadBriefPage } from './c3-brief-data.js';
@@ -66,7 +67,7 @@ export function phoneWanted(store) {
66
67
  }
67
68
 
68
69
  export function mountPhone(root, ctx = {}) {
69
- ensureC3Css(); ensureCss();
70
+ ensureC3Css(); ensureComponentsCss(); ensureCss();
70
71
  const demo = !!ctx.demo;
71
72
  const q = new URLSearchParams(location.search);
72
73
  const at = demo && /^\d{1,2}:\d{2}$/.test(q.get('at') || '') ? q.get('at').split(':').map(Number) : null;
@@ -220,6 +221,15 @@ export function mountPhone(root, ctx = {}) {
220
221
  return out;
221
222
  }
222
223
  const decisions = () => waitRows().filter((r) => !r.pm).length;
224
+ /* the top points of the desktop Briefing (GET /api/briefing cards, the deck), at most three, each opening its details */
225
+ function briefPoints() {
226
+ const cards = (S.briefing && Array.isArray(S.briefing.cards) ? S.briefing.cards : []).filter((c) => c && c.headline);
227
+ return cards.slice(0, 3).map((c, i) => {
228
+ const v = cardView(c);
229
+ return { id: String(c.key || 'card' + i), pm: false, decide: !!(c.move && c.move.options), headline: v.headline,
230
+ label: v.tag + (v.deadline && v.deadline.text ? ' · ' + v.deadline.text : ''), why: c.why && c.why.text, url: c.url, urlLabel: c.url_label, facts: v.facts };
231
+ });
232
+ }
223
233
  const needOpts = { onDone: (r) => itemDone(r), onLater: (r) => itemLater(r, 'later-today') };
224
234
  const focus = (m) => import('./c3-focus.js').then((f) => f.openFocus({ event: { id: m.id, start: m.start, at: m.start, end: m.end || null, title: m.title }, date: String(m.start || '').slice(0, 10), phone: true }))
225
235
  .catch(() => toast('Focus did not open.'));
@@ -316,11 +326,18 @@ export function mountPhone(root, ctx = {}) {
316
326
  if (mo.kind === 'recap') return recapCard();
317
327
  if (mo.kind === 'night') return nightCard();
318
328
  if (mo.kind === 'morning') {
329
+ /* the morning is the briefing (mockup version 7, "Your morning"): the top points of the desktop Briefing's deck, then the first meeting */
319
330
  const w = waitRows(); const late = w.filter((r) => /overdue|late/i.test(r.label)).length;
320
- return card(true, { 'data-c3ph-moment': 'morning' }, why('Your day'), big(mo.next ? mo.next.title + ' at ' + hm(mo.next.start) : 'No meetings today'),
321
- line([mo.next ? 'First up, ' + inText((Date.parse(mo.next.start) - now().getTime()) / 60000) + '.' : null,
322
- w.length ? w.length + (w.length === 1 ? ' thing waits on you' : ' things wait on you') + (late ? ', ' + late + ' overdue.' : '.') : 'Nothing waits on you.'].filter(Boolean).join(' ')),
323
- mo.next ? acts(btn('The brief of this meeting', 'outline', 'calendar', { 'data-c3ph-meeting-open': mo.next.id }, () => meetingSheet(mo.next))) : null);
331
+ const pts = briefPoints();
332
+ const rv = S.briefing && Array.isArray(S.briefing.review) ? S.briefing.review.length : 0;
333
+ return card(true, { 'data-c3ph-moment': 'morning', 'data-c3ph-brief-points': String(pts.length) }, why('Your morning'),
334
+ big(!S.briefing ? 'Reading your briefing' : pts.length ? (pts.length === 1 ? 'One thing this morning' : ['', '', 'Two', 'Three'][pts.length] + ' things this morning') : 'Nothing pressing this morning'),
335
+ pts.length ? el('div', { class: 'c3ph-pts' }, ...pts.map((r) => row({ title: r.headline, meta: [r.label], compact: true, onOpen: () => openNeed(r, needOpts), openLabel: 'Open ' + r.headline, attrs: { 'data-c3ph-point': r.id } }))) : null,
336
+ line([w.length ? w.length + (w.length === 1 ? ' thing waits on you' : ' things wait on you') + (late ? ', ' + late + ' overdue.' : '.') : 'Nothing waits on you.',
337
+ rv ? (rv === 1 ? '1 draft waits' : rv + ' drafts wait') + ' for your review on your computer.' : null].filter(Boolean).join(' ')),
338
+ mo.next ? el('div', { class: 'c3ph-fit', 'data-c3ph-first': mo.next.id }, el('b', null, 'First up: ' + mo.next.title + ' at ' + hm(mo.next.start)),
339
+ line(inText((Date.parse(mo.next.start) - now().getTime()) / 60000).replace(/^in/, 'In') + (who(mo.next) ? ' · ' + who(mo.next) : '')),
340
+ acts(btn('The brief of this meeting', 'outline', 'calendar', { 'data-c3ph-meeting-open': mo.next.id }, () => meetingSheet(mo.next)))) : line('No meetings today.'));
324
341
  }
325
342
  return recapCard();
326
343
  }