create-pmos 0.221.0 → 0.223.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-initiatives-test.js +4 -0
- package/workspace/guide/initiative-boards.md +1 -1
- package/workspace/guide/my-work.md +1 -1
- package/workspace/package-lock.json +2 -2
- package/workspace/package.json +1 -1
- package/workspace/public/pm/c3-doc-sheet.js +5 -2
- package/workspace/public/pm/c3-initiatives.css +11 -0
- package/workspace/public/pm/c3-initiatives.js +7 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.223.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-14T05:
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-14T05:39:31Z"}
|
|
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-14T05:
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-14T05:39:31Z"}
|
|
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-14T05:
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-14T05:39:31Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -141,6 +141,10 @@ const { harness } = require('./c3-ini-harness');
|
|
|
141
141
|
await page.click('[data-ini="edit-workflow"]');
|
|
142
142
|
await until(() => st(() => !!document.querySelector('[data-ini="wf-editor"]')));
|
|
143
143
|
ok('W1 the editor offers only the templates the board offers', await st(() => !document.querySelector('[data-wf-tpl="fix"]').disabled && document.querySelector('[data-wf-tpl="project"]').disabled));
|
|
144
|
+
const wfRow = await st(() => { const r = document.querySelector('[data-wf-row="0"]'); const top = (s) => Math.round(r.querySelector(s).getBoundingClientRect().top); const nb = r.querySelector('.nb');
|
|
145
|
+
return { grip: top('[data-wf-drag]'), name: top('[data-wf-name]'), del: top('[data-wf-del]'), dup: [...nb.querySelectorAll('.chips > *')].some((c) => c.textContent.trim() === document.querySelector('[data-wf-cat="0"]').value),
|
|
146
|
+
mono: getComputedStyle(document.querySelector('[data-ini="wf-editor"] [role="group"] > span')).fontFamily !== getComputedStyle(document.querySelector('[data-ini="wf-editor"]')).fontFamily }; });
|
|
147
|
+
ok('W1b each stage is one row (grip, name and remove on one line); no chip repeats the category; the template label is not mono', Math.abs(wfRow.grip - wfRow.del) < 8 && Math.abs(wfRow.name - wfRow.del) < 8 && !wfRow.dup && !wfRow.mono, JSON.stringify(wfRow));
|
|
144
148
|
await page.focus('[data-wf-down="0"]'); await page.keyboard.press('Enter');
|
|
145
149
|
const order = await st(() => [...document.querySelectorAll('[data-wf-name]')].map((i) => i.value).join(','));
|
|
146
150
|
ok('W2 a stage moves down from the keyboard', order.startsWith('Plan,Discover'), order);
|
|
@@ -86,7 +86,7 @@ Press the gear in the board header to open the board settings.
|
|
|
86
86
|
|
|
87
87
|
## An initiative
|
|
88
88
|
|
|
89
|
-
Each initiative has its own workflow, picked from a template: for example Product, Quick fix, Research only, or Content and launch. The stepper shows the stages of that workflow. Press a stage to move the initiative there. The move is your decision, so nothing blocks it. An order rule shows as a warning chip next to the status, at the right of the breadcrumb, for example "Skipped Plan" or "No spec yet". Press the gear next to the stepper to change the template or edit the stages. When you remove a stage that holds the initiative, the editor asks where it goes.
|
|
89
|
+
Each initiative has its own workflow, picked from a template: for example Product, Quick fix, Research only, or Content and launch. The stepper shows the stages of that workflow. Press a stage to move the initiative there. The move is your decision, so nothing blocks it. An order rule shows as a warning chip next to the status, at the right of the breadcrumb, for example "Skipped Plan" or "No spec yet". Press the gear next to the stepper to change the template or edit the stages. When you remove a stage that holds the initiative, the editor asks where it goes. In the editor, each stage is one row: drag handle, category colour, name, category, then move up, move down and remove. A short note under the name shows only when it adds something, for example **current stage**, **new**, or the old name of a renamed stage.
|
|
90
90
|
|
|
91
91
|
Press **Tools**, at the right of the stepper, to open a tool. The menu lists the tools of the current stage first, then the tools of the other stages. The **…** menu at the right of the breadcrumb opens the Dashboard and the Jira epics of the initiative, and holds Status and Tidy up. The tabs are **Overview**, **Breakdown**, **Docs**, **Tickets**, **Timeline** and **History**.
|
|
92
92
|
|
|
@@ -121,7 +121,7 @@ The row chip then shows the agent's state:
|
|
|
121
121
|
|
|
122
122
|
The page reads the state again every 15 seconds while an agent is queued, working or posting.
|
|
123
123
|
|
|
124
|
-
When the draft is ready, the case file shows it as a document card, with its headings, lists and tables. Select **Open** to read it in the document sheet, the same sheet as your other documents: **Read
|
|
124
|
+
When the draft is ready, the case file shows it as a document card, with its headings, lists and tables. Select **Open** to read it in the document sheet, the same sheet as your other documents: **Read** (with a box under the draft to write to the agent), **Edit**, **Proposals** (the same box) and **Edit log** (the conversation with the agent). Select **Copy** to copy the text.
|
|
125
125
|
|
|
126
126
|
Under the draft is the conversation with the agent: your ask, each draft, each note you sent back and each edit. Select **View round** on an earlier draft to read that version. The conversation stays on the item on the next days too.
|
|
127
127
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmos-workspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.223.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.223.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@xterm/addon-fit": "^0.11.0",
|
package/workspace/package.json
CHANGED
|
@@ -734,7 +734,9 @@ export function openDocSheet({ ws, doc, mode = 'read', prompt = '', relations, o
|
|
|
734
734
|
const adds = openProps().flatMap((p) => p.sections.filter((c) => c.kind === 'added' && S.preview[p.id]).map((c) => el('div', { class: 'ds-sec a prev', 'data-dsec-new': c.name },
|
|
735
735
|
el('div', { class: 'ds-pin' }, el('span', null, c3ic('spark', 'sm'), 'The proposal adds a section: ' + c.name)), el('div', { class: 'ds-sec-h' }, el('h2', null, c.name)), md(diffMd(null, c.after), 'ds-body'))));
|
|
736
736
|
const copyNote = R && R.copyNotice ? el('div', { class: 'fold', 'data-rd-copynote': '' }, c3ic('info', 'sm'), ' The deck now lives in PMOS. The Google Slides copy of this report is optional. ', btn('chip2 lk', {}, 'Copies in other tools', () => toggleCopies())) : null;
|
|
737
|
-
|
|
737
|
+
// a draft on a Work item: the box that writes to the agent sits under the draft (amendment 16)
|
|
738
|
+
const main = [generated ? el('div', { class: 'fold', role: 'note', 'data-doc-generated': '' }, c3ic('info', 'sm'), generated.why) : null, copyNote, staleBanner(), propsBlock(secs, false), cells, adds,
|
|
739
|
+
generated && generated.ask ? askGenerated() : null];
|
|
738
740
|
return el('div', { class: 'ds' + (S.who ? ' who' : '') }, bar, legend,
|
|
739
741
|
olOn ? el('div', { class: 'ds-rlay' + (S.ol ? '' : ' nool') + (S.olOpen ? ' olopen' : '') }, outlineNav(items), el('div', { class: 'ds-rmain' }, main)) : el('div', { class: 'ds-rmain' }, main));
|
|
740
742
|
}
|
|
@@ -1080,7 +1082,8 @@ export function openDocSheet({ ws, doc, mode = 'read', prompt = '', relations, o
|
|
|
1080
1082
|
if (!r || !r.ok) { S.err = (r && r.error) || 'The draft was not sent back.'; draw(); return; }
|
|
1081
1083
|
S.prompt = ''; S.err = ''; ui.close();
|
|
1082
1084
|
};
|
|
1083
|
-
|
|
1085
|
+
ta.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey && !e.isComposing) { e.preventDefault(); send(); } });
|
|
1086
|
+
return el('div', { class: 'ds', 'data-doc-proposals': '' }, el('div', { class: 'ds-ask' }, el('label', { class: 'ds-h', for: 'dpr' }, c3ic('reply', 'sm'), 'Write to the agent'), ta,
|
|
1084
1087
|
el('div', { class: 'ds-acts' }, btn('ds-btn ag', { 'data-doc-ask': '', disabled: S.busy, 'aria-disabled': S.busy ? 'true' : null }, [c3ic('reply', 'sm'), 'Send to the agent'], send)),
|
|
1085
1088
|
S.err ? el('div', { class: 'fold w', role: 'alert' }, c3ic('alert', 'sm'), S.err) : null,
|
|
1086
1089
|
el('p', { class: 'ds-note', 'data-doc-ask-why': '' }, generated.askWhy || 'The agent drafts it again with your note. Nothing is sent anywhere.')));
|
|
@@ -125,6 +125,17 @@
|
|
|
125
125
|
.c3 .ini-kb .nrow.ini-dragging{opacity:.4}
|
|
126
126
|
.c3 [data-ini-col].over,.c3 [data-wf-row].over{outline:2px dashed var(--acc);outline-offset:-2px}
|
|
127
127
|
.c3 .ini-grip{cursor:grab}
|
|
128
|
+
/* the workflow editor's stage rows (sheet body is a .brief, whose rows have two tracks): one line each, grip, swatch, name, category, up, down, remove */
|
|
129
|
+
.c3 [data-ini="wf-list"] .nrow{grid-template-columns:auto minmax(0,1fr) auto;align-items:start;cursor:default;padding-block:6px}
|
|
130
|
+
.c3 [data-ini="wf-list"] .nrow .nb{padding-right:0}
|
|
131
|
+
.c3 [data-ini="wf-list"] .nrow>.acts{display:flex;gap:6px}
|
|
132
|
+
.c3 [data-ini="wf-list"] .nrow .form{flex-wrap:nowrap}
|
|
133
|
+
.c3 [data-ini="wf-list"] .nrow .form input[type=text]{flex:1 1 auto;width:auto;min-width:0}
|
|
134
|
+
.c3 [data-ini="wf-list"] .nrow .form select{flex:none}
|
|
135
|
+
.c3 [data-ini="wf-list"] .nrow .nb>.chips{padding-left:14px}
|
|
136
|
+
.c3 [data-ini="wf-list"] .nrow .nb>.chips>:nth-child(n+3){display:inline-flex !important}
|
|
137
|
+
.c3.ph [data-ini="wf-list"] .nrow{grid-template-columns:auto minmax(0,1fr)}
|
|
138
|
+
.c3.ph [data-ini="wf-list"] .nrow>.acts{grid-column:2}
|
|
128
139
|
|
|
129
140
|
/* ══ the timeline (Portfolio, Board, Initiative) ══ */
|
|
130
141
|
.c3 .ini-tl{position:relative;--lw:240px;display:grid;gap:var(--sp-2);min-width:0}
|
|
@@ -609,7 +609,7 @@ export function mountInitiatives(pane, opts) {
|
|
|
609
609
|
const m = mv();
|
|
610
610
|
put(host,
|
|
611
611
|
note('The ordered stages of ' + row.id + ' only. Other initiatives on ' + b.name + ' keep their own. Each stage sits in one category so the portfolio can compare. No conditions or validators, by design: moving work is your call.'),
|
|
612
|
-
el('div', { class: 'chips2', role: 'group', 'aria-label': 'Start from a template' },
|
|
612
|
+
el('div', { class: 'chips2', role: 'group', 'aria-label': 'Start from a template' }, el('span', { class: 'dim' }, 'Start from a template'),
|
|
613
613
|
...Object.keys(TEMPLATES).map((k) => pill({ 'aria-pressed': String(W.tpl === k), 'data-wf-tpl': k, disabled: !offer.includes(k), title: offer.includes(k) ? TEMPLATES[k].use : templateBuilds(k) && b.build === false ? 'Needs Build and deliver on the board' : 'Not offered on this board',
|
|
614
614
|
onclick: () => { W.tpl = k; W.draft = TEMPLATES[k].stages.map((s) => ({ key: 'k' + (++seq), name: s.name, cat: s.cat, was: now.some((o) => o.name === s.name) ? s.name : null })); W.confirm = false; W.err = ''; render('[data-wf-tpl="' + k + '"]'); } }, TEMPLATES[k].name))),
|
|
615
615
|
b.build === false ? note(b.name + ' has Build and deliver off, so Build and Deliver stages are not offered. Turn it on in the board settings to use them.') : null,
|
|
@@ -643,7 +643,8 @@ export function mountInitiatives(pane, opts) {
|
|
|
643
643
|
render('[data-wf-name="0"]');
|
|
644
644
|
}
|
|
645
645
|
|
|
646
|
-
/** The stage rows of an editor: grip (drag to reorder), name, category,
|
|
646
|
+
/** The stage rows of an editor, one line each: grip (drag to reorder), category swatch, name, category, up, down, remove; under the name only
|
|
647
|
+
* the chips that add something (current stage, was, new, counts, a Build warning). o.render is set by the caller. */
|
|
647
648
|
function stageRows(W, o) {
|
|
648
649
|
let dragFrom = null;
|
|
649
650
|
const api = { render: () => {} };
|
|
@@ -652,10 +653,10 @@ export function mountInitiatives(pane, opts) {
|
|
|
652
653
|
grip.addEventListener('dragstart', (e) => { dragFrom = k; try { e.dataTransfer.setData('text/plain', 'wf'); e.dataTransfer.effectAllowed = 'move'; const r = grip.closest('[data-wf-row]'); if (r) e.dataTransfer.setDragImage(r, 10, 10); } catch { /* no data transfer */ } });
|
|
653
654
|
const li = el('li', { class: 'nrow', 'data-wf-row': String(k) }, grip,
|
|
654
655
|
el('div', { class: 'nb' },
|
|
655
|
-
el('div', { class: 'form' },
|
|
656
|
+
el('div', { class: 'form' }, sw(s.cat),
|
|
656
657
|
el('input', { type: 'text', value: s.name, 'aria-label': 'Stage ' + (k + 1) + ' name', 'data-wf-name': String(k), oninput: (e) => { s.name = e.target.value; } }),
|
|
657
658
|
el('select', { 'aria-label': 'Stage ' + (k + 1) + ' category', 'data-wf-cat': String(k), onchange: (e) => { s.cat = e.target.value; o.onChange(); } }, ...CATS.map((c) => el('option', { value: c, selected: c === s.cat }, c)))),
|
|
658
|
-
|
|
659
|
+
chipsOf(o.current && s.was === o.current ? chip('current stage') : null,
|
|
659
660
|
s.was ? (s.was !== s.name.trim() ? chip('was ' + s.was) : null) : tag('new'),
|
|
660
661
|
o.counts && s.was ? chip(plural(Number(o.counts[s.was]) || 0, 'initiative')) : null,
|
|
661
662
|
!o.build && isBuildStage(s) ? st('warn', 'alert', 'needs Build and deliver') : null)),
|
|
@@ -669,6 +670,7 @@ export function mountInitiatives(pane, opts) {
|
|
|
669
670
|
li.addEventListener('dragend', () => { dragFrom = null; li.classList.remove('over'); });
|
|
670
671
|
return li;
|
|
671
672
|
}), { 'data-ini': 'wf-list' });
|
|
673
|
+
const chipsOf = (...c) => (c.some(Boolean) ? el('div', { class: 'chips' }, ...c) : null);
|
|
672
674
|
const focusIn = (a, b2) => { const sheetEl = document.querySelector('[data-ini="wf-editor"]'); if (!sheetEl) return; const f = sheetEl.querySelector(a); if (f && !f.disabled) f.focus(); else { const g = sheetEl.querySelector(b2); if (g) g.focus(); } };
|
|
673
675
|
return api;
|
|
674
676
|
}
|
|
@@ -692,7 +694,7 @@ export function mountInitiatives(pane, opts) {
|
|
|
692
694
|
const gone = removed().filter((n) => Number(counts[n]) > 0);
|
|
693
695
|
put(host,
|
|
694
696
|
note('The stages that ' + b.name + '\'s initiatives run when they have no workflow of their own. Each initiative can still pick its own from the templates the board offers.'),
|
|
695
|
-
el('div', { class: 'chips2', role: 'group', 'aria-label': 'Start from a template' },
|
|
697
|
+
el('div', { class: 'chips2', role: 'group', 'aria-label': 'Start from a template' }, el('span', { class: 'dim' }, 'Start from a template'),
|
|
696
698
|
...BOARD_TEMPLATES.map((k) => pill({ 'aria-pressed': String(W.tpl === k), 'data-wf-tpl': k, disabled: b.build === false && templateBuilds(k), onclick: () => { W.tpl = k; W.draft = TEMPLATES[k].stages.map((s) => ({ key: 'k' + (++seq), name: s.name, cat: s.cat, hint: '', was: old.includes(s.name) ? s.name : null })); render('[data-wf-tpl="' + k + '"]'); } }, TEMPLATES[k].name))),
|
|
697
699
|
rowsUi.list(),
|
|
698
700
|
el('div', { class: 'chips2' }, pbtn('outline', 'Add stage', 'plus', { 'data-wf-add': '', onclick: () => { W.draft.push({ key: 'k' + (++seq), name: 'New stage', cat: 'Do', hint: '', was: null }); render('[data-wf-name="' + (W.draft.length - 1) + '"]'); } })),
|