create-pmos 0.137.0 → 0.138.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 +1 -1
- package/payload/KIT-VERSION +1 -1
- package/payload/planning/evals/eval-calibration.md +1 -1
- package/payload/planning/okrs/health-budgets.md +1 -1
- package/payload/state/discoveries.md +1 -1
- package/workspace/c3-briefing-test.js +12 -18
- package/workspace/guide/briefing.md +2 -2
- package/workspace/package-lock.json +2 -2
- package/workspace/package.json +1 -1
- package/workspace/public/pm/briefing-deck.js +13 -35
- package/workspace/public/pm/c3-shell.js +2 -2
- package/workspace/public/pm/console3.css +0 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.138.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": {
|
package/payload/KIT-VERSION
CHANGED
|
@@ -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-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-12T01:10:16Z"}
|
|
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-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-12T01:10:16Z"}
|
|
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-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-12T01:10:16Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Loads /c3.html?demo=1 (demo mode answers every /api call, so nothing reaches the server) and holds:
|
|
5
5
|
* R1 two columns: stance + deck + Needs you on the left; Today + Portfolio pulse on the right
|
|
6
6
|
* R2 Review, Handled, What I could stop and Data you can lean on are not on the Briefing
|
|
7
|
-
* R3 the greeting
|
|
7
|
+
* R3 the greeting and the lens line follow the clock; there is no day-part switch (initiative c3-daypart-auto-only)
|
|
8
8
|
* R4 Today: a lead line with a ring, "Open calendar", rows with chips (nrow), no old two-column prepared/before layout
|
|
9
9
|
* R5 Needs you: "N decisions . M of yours" header, decision tags, a Work home chip on own items
|
|
10
10
|
* R6 Portfolio pulse: "N need attention", rows, "All initiatives" goes to Initiatives; 400 wide has no sideways scroll
|
|
@@ -74,22 +74,16 @@ process.on('unhandledRejection', (e) => { console.error(e); finish(1); });
|
|
|
74
74
|
ok('R2 no Review, Handled, Stop or Data trust on the Briefing', await p.evaluate(() => !document.querySelector('[data-c3pg="review-host"], [data-c3pg="handled-host"], [data-c3pg="stop-host"], [data-c3pg="trust-host"]') && !/Data you can lean on|What I could stop|handled for you/i.test(document.querySelector('.c3 .brief').textContent)));
|
|
75
75
|
|
|
76
76
|
/* R3 */
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
ok('R3
|
|
86
|
-
await p.
|
|
87
|
-
const dn = await until(async () => { const d = await dp(); return d.on[0] === 'night' ? d : null; }, 4000);
|
|
88
|
-
ok('R3 Night previews: "Good night" and the Reflect line', !!dn && /^Good night/.test(dn.greet) && /^Reflect/.test(dn.line), JSON.stringify(dn));
|
|
89
|
-
ok('R3 the Night preview names Needs you "Carries over"', await p.evaluate(() => /Carries over/i.test(document.querySelector('[data-c3pg="needs"] .sh2 h2').textContent)));
|
|
90
|
-
await p.click(`.stance [data-pg-dp="${clockPart}"]`);
|
|
91
|
-
const d2 = await until(async () => { const d = await dp(); return d.on[0] === clockPart ? d : null; }, 4000);
|
|
92
|
-
ok('R3 a tap on the clock part brings it back', !!d2 && d2.greet === d0.greet, JSON.stringify(d2));
|
|
77
|
+
const d0 = await p.evaluate(() => {
|
|
78
|
+
const h = new Date().getHours();
|
|
79
|
+
const k = h >= 5 && h < 11 ? 'morning' : h >= 11 && h < 14 ? 'midday' : h >= 14 && h < 17 ? 'afternoon' : h >= 17 && h < 21 ? 'evening' : 'night';
|
|
80
|
+
const st = document.querySelector('.stance');
|
|
81
|
+
return { k, part: st.dataset.part, greet: st.querySelector('b').textContent, line: st.querySelector('.dim').textContent, buttons: document.querySelectorAll('[data-pg-dp], .seg.dp').length };
|
|
82
|
+
});
|
|
83
|
+
const want = { morning: ['Good morning', 'Plan'], midday: ['Good afternoon', 'Unblock'], afternoon: ['Good afternoon', 'Close'], evening: ['Good evening', 'Reflect'], night: ['Good night', 'Reflect'] }[d0.k];
|
|
84
|
+
ok('R3 no day-part switch on the page', d0.buttons === 0, JSON.stringify(d0));
|
|
85
|
+
ok('R3 the stance follows the clock: its part, greeting and lens line', d0.part === d0.k && d0.greet.startsWith(want[0] + ', ') && d0.line.startsWith(want[1] + ':'), JSON.stringify(d0));
|
|
86
|
+
ok('R3 at night or in the evening Needs you reads "Carries over"', want[1] !== 'Reflect' || await p.evaluate(() => /Carries over/i.test(document.querySelector('[data-c3pg="needs"] .sh2 h2').textContent)));
|
|
93
87
|
|
|
94
88
|
/* R4 */
|
|
95
89
|
const td = await p.evaluate(() => {
|
|
@@ -123,7 +117,7 @@ process.on('unhandledRejection', (e) => { console.error(e); finish(1); });
|
|
|
123
117
|
|
|
124
118
|
const M = await open({ viewport: { width: 400, height: 900 } });
|
|
125
119
|
ok('R6 at 400 wide the Briefing does not scroll sideways', M.up && await M.page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 1 && document.getElementById('c3-root').scrollWidth <= innerWidth + 1), 'scrollWidth');
|
|
126
|
-
ok('R6 at 400 wide the
|
|
120
|
+
ok('R6 at 400 wide the stance and Needs you are on the page, with no day-part switch', await M.page.evaluate(() => !!document.querySelector('.stance') && document.querySelectorAll('[data-pg-dp]').length === 0 && !!document.querySelector('[data-c3pg="needs"]')));
|
|
127
121
|
ok('0 no page error (400 wide)', M.errors.length === 0, M.errors.join(' | '));
|
|
128
122
|
await M.ctx.close();
|
|
129
123
|
return finish();
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Guide
|
|
3
3
|
title: "The Briefing: today's brief"
|
|
4
4
|
description: "The Briefing page of the console (/c3.html): the headline cards, Today, Needs you, Portfolio pulse, and what Done, Not yet and Drop do to an item."
|
|
5
|
-
timestamp: 2026-10-
|
|
5
|
+
timestamp: 2026-10-12
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# The Briefing: today's brief
|
|
@@ -17,7 +17,7 @@ The Briefing shows only what PMOS already holds. When PMOS holds nothing for a s
|
|
|
17
17
|
|
|
18
18
|
The cards at the top are the signals that pass the relevance bar. Turn a card over to read its case.
|
|
19
19
|
|
|
20
|
-
Above the cards, the greeting names the part of the day, for example "Good morning, Wawan". The name is the first word of the git user name on the computer where PMOS runs. When git holds no name, the greeting has none.
|
|
20
|
+
Above the cards, the greeting names the part of the day, for example "Good morning, Wawan". The name is the first word of the git user name on the computer where PMOS runs. When git holds no name, the greeting has none. The part of the day follows the clock. There is no button to change it. When the clock moves to a new part, the line under the greeting and the order of the cards change after you stop using the page for a minute.
|
|
21
21
|
|
|
22
22
|
## Today
|
|
23
23
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmos-workspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.138.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pmos-workspace",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.138.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@xterm/addon-fit": "^0.11.0",
|
package/workspace/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* pm/briefing-deck.js — the Briefing view (console3 wave C1), built on the console3 mockup's own markup and CSS.
|
|
2
|
-
* The mockup (prototype/pmos-console-handoff/mockups/console3.html) is the UI: the stance bar
|
|
3
|
-
*
|
|
2
|
+
* The mockup (prototype/pmos-console-handoff/mockups/console3.html) is the UI: the stance bar (.stance; the part of the day and its
|
|
3
|
+
* lens follow the clock, with no switch), the running line (.tick, .tkt), the card deck (.deck, .fc, .cf, .hero, .dk, .dkd), the flip to the case file (.fb,
|
|
4
4
|
* .case, .chgl, .opts, .slt, .cft), the tilt and the flip animation. The styles are pm/console3.css (see pm/console3-lift.md).
|
|
5
5
|
* This file keeps the data call (GET /api/briefing, the signal engine's cards) and the model (briefing-model.js) and draws
|
|
6
6
|
* what the engine sent: at most five cards, none invented. A field the engine does not supply is left out.
|
|
@@ -12,12 +12,11 @@
|
|
|
12
12
|
* Not registered with the registry (Today has no view registry): dest-today.js imports mountBriefing/disposeBriefing. */
|
|
13
13
|
import { el } from './dom.js';
|
|
14
14
|
import { createIdleGate, watchIdle, applyDayPart } from './c3-daypart.js';
|
|
15
|
-
import {
|
|
15
|
+
import { TAG_STYLE, PART_STYLE, partByKey, partFor, cardView, nextCards, swipeDir, sizeFor, caseModelOf, rawOption } from './briefing-model.js';
|
|
16
16
|
import { BX, caseFile, toast } from './c3-brief.js';
|
|
17
17
|
import { ensureC3Css, c3ic, c3ring, c3Phone } from './pm-work-view.js';
|
|
18
18
|
import { listenButton } from './shell-readaloud.js';
|
|
19
19
|
import { onBriefPage } from './c3-brief-data.js';
|
|
20
|
-
import { button } from './primitives.js';
|
|
21
20
|
|
|
22
21
|
const NS = 'http://www.w3.org/2000/svg';
|
|
23
22
|
const roots = new WeakMap(); // host -> state
|
|
@@ -336,41 +335,21 @@ async function load(S, lens) {
|
|
|
336
335
|
S.data[lens] = null;
|
|
337
336
|
S.data[lens] = await readLens(lens, S.zoom, S.ctx && S.ctx.scope);
|
|
338
337
|
if (S.dead) return; // a deck built before its host is attached still draws: it only touches its own root
|
|
339
|
-
if (lens === S.lens) draw(S);
|
|
340
|
-
else { const old = S.root.querySelector('.stance'); if (old) old.replaceWith(stance(S)); } // another lens arrived: update the switch, keep the open card
|
|
338
|
+
if (lens === S.lens) draw(S); // a lens the clock has since left stays read for later, undrawn
|
|
341
339
|
}
|
|
342
340
|
|
|
343
|
-
/* the stance bar (the mockup's stanceBar): the greeting for the part of the day, the lens and its line,
|
|
344
|
-
*
|
|
345
|
-
*
|
|
341
|
+
/* the stance bar (the mockup's stanceBar): the icon and greeting for the part of the day, the lens and its line, and the read-aloud button.
|
|
342
|
+
* The part follows the clock and nothing else: there is no switch (the PM's feedback, initiative c3-daypart-auto-only). The name is the
|
|
343
|
+
* PM's first name when PMOS holds one (GET /api/brief-page `me`); without one the greeting stands alone. */
|
|
346
344
|
function stance(S) {
|
|
347
345
|
const P = PART_STYLE[S.lens] || PART_STYLE.Plan;
|
|
348
|
-
const part = partByKey(S.
|
|
349
|
-
|
|
350
|
-
...PARTS.map((x) => {
|
|
351
|
-
const b = button({ label: x.label, variant: 'ghost', size: 'sm', onClick: () => setPart(S, x.k), attrs: { 'aria-pressed': String(x.k === part.k), 'data-pg-dp': x.k, title: x.label + ' · ' + x.at + (x.k === S.clockPart ? ' (now)' : ''), 'aria-label': x.label } });
|
|
352
|
-
b.replaceChildren(c3ic(x.icon, 'sm')); // the icon alone; the label is the aria-label
|
|
353
|
-
return b;
|
|
354
|
-
}));
|
|
355
|
-
return el('div', { class: 'stance', 'data-bf': 'lens-bar' },
|
|
346
|
+
const part = partByKey(S.clockPart);
|
|
347
|
+
return el('div', { class: 'stance', 'data-bf': 'lens-bar', 'data-part': part.k },
|
|
356
348
|
el('span', { class: 'ki' }, c3ic(part.icon)),
|
|
357
349
|
el('div', null, el('b', { title: 'Follows the clock: this is the part of the day it is now.' }, part.greet + (S.me ? ', ' + S.me : '')), el('span', { class: 'dim' }, S.lens + ': ' + P.sub)),
|
|
358
|
-
seg,
|
|
359
350
|
listenButton(() => { const x = S.data[S.lens]; return x && x.ok ? x.cards[S.i] || x.cards[0] : null; })); // the mockup's speaker (data-a=listen) reads the card on top
|
|
360
351
|
}
|
|
361
352
|
|
|
362
|
-
/* Preview one part of the day: its lens, its cards. Nothing is saved; the clock's own part comes back by tapping it, or when the clock moves on. */
|
|
363
|
-
function setPart(S, k) {
|
|
364
|
-
const part = partByKey(k);
|
|
365
|
-
S.preview = k === S.clockPart ? null : k;
|
|
366
|
-
const lens = part.lens;
|
|
367
|
-
const changed = lens !== S.lens;
|
|
368
|
-
S.lens = lens; S.i = 0; S.flip = null;
|
|
369
|
-
if (changed) load(S, lens);
|
|
370
|
-
draw(S);
|
|
371
|
-
if (S.ctx.onLens) S.ctx.onLens(lens);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
353
|
/* the mockup's running line (.tick > .tkt, lines 539 to 545): the engine's ticker strings, twice so the marquee loops */
|
|
375
354
|
function ticker(items) {
|
|
376
355
|
const one = (hide) => items.map((t) => el('span', { class: 'tki', 'aria-hidden': hide ? 'true' : null }, el('b', { style: 'color:var(--acc)' }, c3ic('bolt', 'sm')), String(t)));
|
|
@@ -416,19 +395,18 @@ export function mountBriefing(host, ctx) {
|
|
|
416
395
|
host.replaceChildren(root);
|
|
417
396
|
const clockPart = partFor(new Date()).k;
|
|
418
397
|
const clock = partByKey(clockPart).lens;
|
|
419
|
-
S = { host, root, ctx: ctx || {}, lens: clock, clock, clockPart,
|
|
398
|
+
S = { host, root, ctx: ctx || {}, lens: clock, clock, clockPart, me: null, zoom: 'week', i: 0, flip: null, data: {}, dead: false };
|
|
420
399
|
S.stopPhone = c3Phone(root);
|
|
421
400
|
roots.set(host, S);
|
|
422
401
|
draw(S);
|
|
423
402
|
if (!host.closest('[data-daypart]')) applyDayPart(root); // a page without the console3 shell still gets the day part tokens
|
|
424
403
|
/* The clock moves the lens, but never under the reader: a new part waits for 60 s without pointer or key activity and for every card to be turned back. */
|
|
425
404
|
const gate = createIdleGate({ current: S.clockPart, part: (ms) => partFor(new Date(ms)).k, busy: () => !!S.flip || !!S.root.querySelector('.fc.back'),
|
|
426
|
-
apply: (k) => { if (S.dead) return; const l = partByKey(k).lens; S.clockPart = k; S.
|
|
405
|
+
apply: (k) => { if (S.dead) return; const l = partByKey(k).lens; S.clockPart = k; S.clock = l; S.lens = l; S.i = 0; S.flip = null; load(S, l); draw(S); if (S.ctx.onLens) S.ctx.onLens(l); } });
|
|
427
406
|
S.idle = watchIdle(gate);
|
|
428
407
|
if (!(S.ctx && S.ctx.bare)) onBriefPage((j) => { const me = j && j.ok !== false && j.me ? String(j.me) : null; if (me === S.me || S.dead) return; S.me = me; const old = S.root.querySelector('.stance'); if (old) old.replaceWith(stance(S)); }); // the greeting's name
|
|
429
408
|
if (S.ctx.onLens) S.ctx.onLens(S.lens); // the page may follow the lens from the start (Reflect: "Carries over", "What I could stop")
|
|
430
|
-
// the
|
|
431
|
-
load(S, S.lens).then(() => Promise.all(LENSES.filter((l) => l !== S.lens).map((l) => load(S, l))));
|
|
409
|
+
load(S, S.lens); // only the clock's lens: the next one is read when the clock moves to it
|
|
432
410
|
}
|
|
433
411
|
|
|
434
412
|
/* Demo: a tab whose deck follows another control (the Work lens) changes the scope the deck asks for. Real mode never calls it. */
|
|
@@ -436,7 +414,7 @@ export function setBriefingScope(host, scope) {
|
|
|
436
414
|
const S = roots.get(host);
|
|
437
415
|
if (!S || S.ctx.scope === scope) return;
|
|
438
416
|
S.ctx.scope = scope; S.data = {}; S.i = 0; S.flip = null; draw(S);
|
|
439
|
-
load(S, S.lens)
|
|
417
|
+
load(S, S.lens);
|
|
440
418
|
}
|
|
441
419
|
|
|
442
420
|
/* Open one card on its case file (the bell's "go to the card", console3.html nopen 2533). True when the card is on this deck. */
|
|
@@ -265,7 +265,7 @@ function moveInd() {
|
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
/* ── the Briefing tab: two columns, decisions and reads ──
|
|
268
|
-
* Left: the stance bar
|
|
268
|
+
* Left: the stance bar (the part of the day follows the clock) and the headline deck (briefing-deck.js), then Needs you (c3-brief-needs.js).
|
|
269
269
|
* Right: Today (c3-brief-today.js) and Portfolio pulse (c3-brief-pulse.js). Review lives in Work's Review lens; Handled, What I could stop and
|
|
270
270
|
* Data you can lean on live in Reports > Drafts (console3-briefing-parity, PM decision 1). The ribbon and the embedded calendar are not here:
|
|
271
271
|
* the calendar is the Calendar tab. */
|
|
@@ -290,7 +290,7 @@ function briefTab(pane) {
|
|
|
290
290
|
loadBriefPage();
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
-
/* The
|
|
293
|
+
/* The clock's part of the day: Reflect names Needs you "Carries over" (the mockup's vBrief, line 1470). */
|
|
294
294
|
function lensChanged(l) {
|
|
295
295
|
setNeedsTitle(S.needsBox, l === 'Reflect');
|
|
296
296
|
}
|
|
@@ -435,7 +435,6 @@
|
|
|
435
435
|
.c3 .stance>div:nth-child(2){flex:1;min-width:0;display:flex;gap:8px;align-items:baseline;flex-wrap:wrap}
|
|
436
436
|
.c3 .stance b{font-size:var(--fs-3)}
|
|
437
437
|
.c3 .stance .dim{font-size:var(--fs-3)}
|
|
438
|
-
.c3 .dp button{padding:4px 8px}
|
|
439
438
|
.c3 .lens{display:flex;align-items:center;gap:6px;max-width:100%}
|
|
440
439
|
.c3 .lens .seg{overflow-x:auto;min-width:0}
|
|
441
440
|
.c3 .lh{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:10px;align-items:center;padding:2px 0 6px}
|
|
@@ -1634,7 +1633,6 @@
|
|
|
1634
1633
|
.c3 .psess .c{font:var(--fs-1) var(--mono)}
|
|
1635
1634
|
/* Briefing as the brief (initiative briefing-brief): one column; Today agenda; own-item rows of Needs you; inline Portfolio pulse */
|
|
1636
1635
|
/* Briefing, console3-briefing-parity: the day-part switch in the stance bar, the tags, the Today rows */
|
|
1637
|
-
.c3 .stance .seg.dp{flex:none}
|
|
1638
1636
|
.c3 .stance{flex-wrap:wrap}
|
|
1639
1637
|
.c3 .tag.acc{background:var(--accw);color:var(--acc)}
|
|
1640
1638
|
.c3 .tag.ok{color:var(--foreground)}
|