create-pmos 0.123.0 → 0.124.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.123.0",
3
+ "version": "0.124.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@d13a7b9
1
+ kernel-source: pmos@eee5377
2
2
  assembled: 2026-10-09
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-09T03:00:14Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-09T03:19:19Z"}
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-09T03:00:14Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-09T03:19:19Z"}
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-09T03:00:14Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-09T03:19:19Z"}
6
6
  status: stable
7
7
  ---
8
8
 
@@ -63,6 +63,7 @@ The Notes view shows one meeting, top to bottom. A bar at the top shows whether
63
63
  1. Press **Listen**. Select what to listen to:
64
64
  - **My microphone**: what you say.
65
65
  - **The meeting**: the other people in an online meeting. The meeting can be in any app, such as Zoom, or in any browser tab. In the sharing window, choose **Entire screen** and tick **Also share system audio**, so that the sound of every app is recorded. To record one browser tab, choose that tab and tick **Share tab audio**. Chrome offers system audio on Windows and ChromeOS; on macOS use a tab.
66
+ - **Skipping the sharing window.** A browser always asks before a page records the screen, and a page cannot turn that off. To pick **Entire screen** without being asked, start Chrome with the switch `--auto-select-desktop-capture-source="Entire screen"` (a Chrome on your own machine, for example from a shortcut). System audio is then shared when the browser and the system offer it (see above). This is a Chrome setting on your machine, not a PMOS setting.
66
67
  2. Press **Start**. The header shows a red dot, the time, and the sources. Tell people that you are taking notes. If the meeting stays completely silent for 20 seconds while your microphone works, a red message says that the other people are not being recorded. Most often the sharing window had the wrong source. Press **Stop**, press **Listen** again, choose **Entire screen** (or the meeting's tab), and tick **Also share system audio** (or **Share tab audio**). The message goes away when sound arrives or when you press **Stop**.
67
68
  3. Each line shows in the transcript a few seconds after it is said, with who said it, the time and its label. Your lines are **You**. The meeting's lines are **Meeting**: click the name to change it. The transcript cannot tell the people in the meeting apart.
68
69
  4. Press **Stop** to end. The summary shows at the top of your notes. To carry on, press **Listen again**: the clock carries on from where it stopped, and the time between does not count. Line times and the kept audio use the same clock.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pmos-workspace",
3
- "version": "0.123.0",
3
+ "version": "0.124.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pmos-workspace",
9
- "version": "0.123.0",
9
+ "version": "0.124.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.123.0",
3
+ "version": "0.124.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",
@@ -207,7 +207,7 @@ async function refresh(inst) {
207
207
  if (seq !== inst.seq) return;
208
208
  if (r && r.ok) { inst.rows = r.notes || []; inst.error = null; } else { inst.rows = inst.rows || []; inst.error = (r && r.error) || COPY.readError; }
209
209
  if (!inst.open && inst.rows.length) inst.open = inst.rows[0].id;
210
- if (one && one.ok && one.note && one.note.id === inst.open) inst.note = one.note;
210
+ if (one && one.ok && one.note && one.note.id === inst.open) { inst.note = one.note; inst.rec = one.recording || null; }
211
211
  else if (inst.open && !one) loadOpen(inst);
212
212
  drawLeft(inst);
213
213
  drawRight(inst);
@@ -217,8 +217,25 @@ async function loadOpen(inst) {
217
217
  const r = await pmGet('/api/notes/one?id=' + encodeURIComponent(id));
218
218
  if (inst.open !== id) return;
219
219
  inst.note = r && r.ok ? r.note : null;
220
+ inst.rec = r && r.ok ? r.recording || null : null;
220
221
  drawRight(inst);
221
222
  }
223
+ /** The replay of the note's kept audio: one <audio> per note, kept across redraws so playback goes on. Null when no audio is kept. */
224
+ function replayFor(inst, n) {
225
+ const rec = inst.rec;
226
+ if (!rec || n.private) { inst.audio = null; return null; }
227
+ const key = n.id + ':' + rec.bytes + ':' + (rec.cleaned ? 1 : 0);
228
+ if (inst.audio && inst.audio.key === key) return inst.audio.el;
229
+ if (inst.audio && !inst.audio.el.paused) return inst.audio.el; // never cut a replay that is playing
230
+ const a = el('audio', { controls: true, preload: 'metadata', class: 'nt-audio', 'data-ntm-audio': '', src: '/api/notes/recording?id=' + encodeURIComponent(n.id) + '&b=' + rec.bytes + (rec.cleaned ? '&c=1' : '') });
231
+ inst.audio = { key, el: a };
232
+ return a;
233
+ }
234
+ function seekReplay(inst, ms) {
235
+ const a = inst.audio && inst.audio.el;
236
+ if (!a) return;
237
+ try { a.currentTime = Math.max(0, ms / 1000); a.play(); } catch { /* the audio is not ready */ }
238
+ }
222
239
  function choose(inst, id) {
223
240
  if (inst.open === id) return;
224
241
  inst.open = id; inst.note = null; inst.ctxOpen = null; inst.info = false;
@@ -259,7 +276,7 @@ function drawRight(inst, force) {
259
276
  const n = inst.note;
260
277
  const live = dock.recordState();
261
278
  const held = !!n && live.mode !== 'idle' && live.id === n.id;
262
- const sig = JSON.stringify([inst.open, n && [n.updated_at, n.title, n.final_notes && n.final_notes.at, (n.transcript || []).length, (n.highlights || []).length, n.agent && n.agent.markdown, iniName(n.initiative), n.state, n.resume_until, n.ended_at, (n.sessions || []).length, n.meeting && n.meeting.context && n.meeting.context.taken_at], live.mode, live.id, !!inst.rows, inst.ctxOpen, inst.info]);
279
+ const sig = JSON.stringify([inst.open, n && [n.updated_at, n.title, n.final_notes && n.final_notes.at, (n.transcript || []).length, (n.highlights || []).length, n.agent && n.agent.markdown, iniName(n.initiative), n.state, n.resume_until, n.ended_at, (n.sessions || []).length, n.meeting && n.meeting.context && n.meeting.context.taken_at], inst.rec && [inst.rec.bytes, inst.rec.cleaned], live.mode, live.id, !!inst.rows, inst.ctxOpen, inst.info]);
263
280
  // a held note draws again on every render: the clock, the lines and the paused minutes move. Nothing else draws without a change.
264
281
  if (!force && !held && sig === inst.rightSig) return;
265
282
  // a field being typed in is not redrawn under the cursor, except in the live block, whose fields are kept across redraws
@@ -387,6 +404,11 @@ function noteParts(inst, n, live) {
387
404
  if (V.renderLive(liveHost, { state: inst.ctx.state, setState: inst.ctx.setState, FIXTURE: inst.ctx.FIXTURE }, n.id)) out.push(liveHost);
388
405
  }
389
406
 
407
+ const replay = held ? null : replayFor(inst, n);
408
+ if (replay) out.push(el('section', { class: 'nt-pg-sec nt-pg-sec--first nt-pg-replay', 'data-ntm': 'replay' },
409
+ el('h3', { class: 'nt-pg-sec__h' }, el('span', null, 'Replay')), replay,
410
+ el('p', { class: 'nt-hint' }, 'Kept on this machine. Press a time in the discussion to play from there.')));
411
+
390
412
  const cf = conflictsOf(n);
391
413
  if (cf.length) {
392
414
  out.push(el('section', { class: 'nt-pg-conflicts', 'data-ntm': 'conflicts' },
@@ -438,7 +460,7 @@ function noteParts(inst, n, live) {
438
460
  out.push(el('details', { class: 'nt-pg-sec nt-pg-fold', 'data-ntm': 'discussion' },
439
461
  el('summary', { class: 'nt-pg-fold__sum' }, COPY.discussion + ' · ' + bits),
440
462
  p.topics.map((tp) => el('div', { class: 'nt-pg-topic' }, el('h4', { class: 'nt-pg-topic__h' }, tp.title), (tp.points || []).map((pt) => el('p', { class: 'nt-pg-topic__p' }, pt)))),
441
- tr.length ? el('div', { class: 'nt-pg-trans' }, tr.map((x) => el('div', { class: 'nt-pg-li' }, el('span', { class: 'nt-pg-li__at' }, V.fmtTime(x.t || 0)), el('span', { class: 'nt-pg-li__text' }, x.text)))) : null));
463
+ tr.length ? el('div', { class: 'nt-pg-trans' }, tr.map((x) => el('div', { class: 'nt-pg-li' }, inst.audio && !held ? button({ label: V.fmtTime(x.t || 0), variant: 'ghost', size: 'sm', onClick: () => seekReplay(inst, x.t || 0), attrs: { 'data-ntm-seek': '', title: 'Play from here' } }) : el('span', { class: 'nt-pg-li__at' }, V.fmtTime(x.t || 0)), el('span', { class: 'nt-pg-li__text' }, x.text)))) : null));
442
464
  }
443
465
  return out.filter(Boolean);
444
466
  }