create-pmos 0.87.0 → 0.89.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.87.0",
3
+ "version": "0.89.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@6a237b2
1
+ kernel-source: pmos@5feb40c
2
2
  assembled: 2026-10-07
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-07T16:37:01Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-07T17:12:14Z"}
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-07T16:37:01Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-07T17:12:14Z"}
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-07T16:37:01Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-07T17:12:14Z"}
6
6
  status: stable
7
7
  ---
8
8
 
@@ -50,16 +50,16 @@ The dock holds your terminals, documents, conversations, skills and web pages. I
50
50
 
51
51
  - Press ⌘J (Ctrl-J), or the button on the rail, to open the dock. Press ⌘J again, or the fold button in the dock, to fold it to the rail.
52
52
  - The dock never opens by itself. A tab that opens while the dock is a rail waits in the dock until you open it.
53
- - The dock lists the sessions of the repository by initiative. A session is in the group of the initiative it is recorded on, or of its `initiative/<id>` branch. Then come **Not linked** (no initiative) and **Automatic** (started by the workspace itself, for example by a schedule or the day run). **Automatic** is hidden until you press **Show automatic** at the foot of the list (or beside the group select in the strip). A session in front, or one that waits on you, is always shown. Finished automatic sessions remove themselves a minute after a clean exit, and one with no output for 60 minutes is ended and removed. A failed one stays. The setting is in `state/session-policy.json`: `finish` (true or false) and `idleMinutes` (5 or more, or 0 for off). Then **Open** lists the tabs that are not sessions, for example documents and skills. Each one shows its title, what it is (for example PRD, Document or Web page) and its branch when it is not main. **✕** on an Open row closes the tab only. A terminal keeps running.
53
+ - The dock lists the sessions of the repository by initiative. A session is in the group of the initiative it is recorded on, or of its `initiative/<id>` branch. Then come **Not linked** (no initiative) and **Automatic** (anything you did not start yourself, for example a schedule, the day run, a note's summary or bullets, or a Jira push). **Automatic** is hidden until you press **Show automatic** at the foot of the list (or beside the group select in the strip). A session in front, or one held on a permission question, is always shown. An agent that has finished and reads "needs you" is hidden like the rest. Finished automatic sessions remove themselves a minute after a clean exit, and one with no output for 60 minutes is ended and removed. A failed one stays. The setting is in `state/session-policy.json`: `finish` (true or false) and `idleMinutes` (5 or more, or 0 for off). Then **Open** lists the tabs that are not sessions, for example documents and skills. Each one shows its title, what it is (for example PRD, Document or Web page) and its branch when it is not main. **✕** on an Open row closes the tab only. A terminal keeps running.
54
54
  - Each session row starts with a mark for its state: ● working, ◼ quiet, ◆ needs you, ◇ maybe blocked, ○ ended. Under the name, the row shows the state when the session is not working, the type of work when one is set, and the branch when it is not main and not the initiative's own branch. Running sessions come first and ended sessions last. Press the row to bring the session to the front.
55
55
  - Press the name of a group to fold it or open it. The number beside it counts its sessions. **Automatic** is folded while none of its sessions is running, because each run leaves an ended session. The group of the session in front always stays open, and the workspace keeps your choice for each group.
56
56
  - **⋯** and **✕** show on the row in front, on the row under the pointer and on the row with the keyboard focus. On a touch screen they always show.
57
57
  - Press **⋯** on a row to change the name, the initiative or the type of work.
58
58
  - Press **✕** on a row to end the session. The row asks "End “name”?". Press **End** or **Cancel**. When you end the last running session on its own working copy, the message offers **Remove working copy**. This removes the folder only. The branch stays. If the folder has unsaved work, the workspace keeps it and tells you why.
59
59
  - **+ Session** at the top of the dock, or **+** on an initiative, opens one sheet. Type a name, or leave it empty to name the session after the skill and the initiative. **For** is the initiative. **On** is where the session runs. **Automatic** uses the initiative's own working copy on `initiative/<id>` (made the first time), or the main tree when there is no initiative. You can also choose a branch that has a working copy, or **New branch…**. Then press **Blank session** (the CLI and model from Settings) or a skill. The session starts at once.
60
- - The icons at the top of the session list, under **+ Session**, show the sessions as a strip on top, give the dock the whole window (Focus), and fold the dock. They stay in view when you scroll the list. On top, a select chooses the group, and each session of the group is a button. **+** starts a session, and **✕** ends the session in front.
60
+ - The icons at the top of the session list, under **+ Session**, switch the sessions as a strip on top or a list at the side, set the dock size, and fold the dock. They stay in view when you scroll the list. On top, a select chooses the group, and each session of the group is a button. **+** starts a session, and **✕** ends the session in front.
61
61
  - To make the dock wider or narrower, drag its left edge. With the edge focused, the left and right arrow keys change the width in steps. Double-click the edge to go back to the default width. The width is kept in this browser.
62
- - **Focus** in the dock gives the dock the whole window. The place and the detail are hidden, and the dock stays full width on every place. Press Focus again, or ⌘⇧J (Ctrl-Shift-J), to show the place again. Folding the dock also ends Focus.
62
+ - **Dock size** opens a menu of four sizes: Normal (the default width), Half the window, Two-thirds, and Whole window (focus). Choose one to set the dock to that size. The size is remembered in this browser. Dragging the edge clears the chosen size and sets a pixel width instead. Whole window (focus) gives the dock the entire screen; the place and the detail are hidden, and the dock stays full width on every place. Press Whole window again, or ⌘⇧J (Ctrl-Shift-J), to show the place. Folding the dock also ends focus.
63
63
  - **Scratchpad** in the bar at the bottom of the dock opens and closes the scratchpad. The pad keeps one note for each terminal session, and one for the repository when no terminal is in front. **Send to terminal** types the note into the session in front and presses Enter. It is available only when a terminal is in front. **Copy** and **Clear** act on the note.
64
64
  - The same bottom bar shows the title of the tab in front and a copy button that names what it copies: **Copy document** for a document, **Copy attach command** for a terminal.
65
65
  - A document tab has one bar. The words at its start say whether the file is saved on disk or has unsaved changes. The copy button beside them shows the file's Confluence or Google Docs copy (see [Edit a document in place](#edit-a-document-in-place)). **Changes**, **Revert** and **Save** show when there are unsaved changes. **Markdown** shows the whole file as markdown in one box; press it again to go back. A file that you cannot edit has **Source** in its place. **⋯** holds the rest: the reading width (full width, or lines of 72 characters), **Normalize line breaks** (it joins hard-wrapped paragraphs into one line each in the buffer; read the result in Changes, then save) and **Hide the outline and connections**. **Run scripts** shows on the bar for an HTML page that can run its own scripts.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  type: Guide
3
3
  title: "People: the people you work with in a repository, their interactions, tone, notes and shared work"
4
- timestamp: 2026-10-06
4
+ timestamp: 2026-10-07
5
5
  ---
6
6
 
7
7
  # People
@@ -37,16 +37,18 @@ A sighting of a new handle adds the person. A sighting with the full name of a p
37
37
 
38
38
  ## The list
39
39
 
40
- Type in **Find a person** to search names, handles and initiatives. **All**, **Team** and **External** filter the list. **Latest**, **Most** and **Name** sort it by the last interaction, the number of interactions or the name. Press a person to open their page beside the list.
40
+ People has the same two-card layout as the Agenda. The title says how many people there are. **All**, **Team** and **External** at the top right filter the list. The left card holds the list. Type in **Find a person** to search names, handles and initiatives. **Latest**, **Most** and **Name** sort it by the last interaction, the number of interactions or the name. Each row shows initials, the name, the role and organisation, and the tone as a coloured dot (a ? means you have not confirmed it). Press a person to open their page in the right card. Each card scrolls by itself.
41
41
 
42
42
  ## A person's page
43
43
 
44
+ The top of the page shows the person's name and role, a **Confirm** button when a tone is waiting, and a **⋯** menu. Three tiles under it give the number of interactions, the tone and the shared work.
45
+
44
46
  - **Interactions.** Newest first. Each line shows the date, the kind (Mail, Chat, Meeting, 1:1, Jira, Doc, Call or Mention), the line, and where it came from with its pointer and tone.
45
- - **Tone.** The mix of tones (warm, neutral, cautious, tense) and a proposed overall tone: the most frequent tone of the latest 10 interactions. When two tones are tied, the latest one wins. Press **Confirm** to keep the proposal, **Set another** to choose a different tone, or **Change** to clear your choice. Tone describes the exchanges, not the person.
47
+ - **Tone.** The mix of tones (warm, neutral, cautious, tense) and a proposed overall tone: the most frequent tone of the latest 10 interactions. When two tones are tied, the latest one wins. Press **Confirm** (at the top or here) to keep the proposal, **Set another** to choose a different tone, or **Change** to clear your choice. Tone describes the exchanges, not the person.
46
48
  - **Shared work.** The initiatives your interactions name, the plan items the person owns (with **Blocked** when the item is blocked), and the coming meetings that name them when calendar names are on. Nothing here is typed by hand.
47
- - **Notes.** Type a note and press **Add note**. Press **Remove** to remove one. Only you can add or remove a note. No command writes one.
49
+ - **Notes.** Type a note and press the **+** button (**Add note**). Press the **×** next to a note to remove it. Only you can add or remove a note. No command writes one.
48
50
  - **Identity and sources.** The person's handles, how many interactions each source gave, where People found them, and the file the person lives in.
49
- - **Remove from People.** Takes the person out. Later sightings of their handles stay out.
51
+ - **Remove from People.** In the **⋯** menu. Takes the person out. Later sightings of their handles stay out.
50
52
 
51
53
  ## Ways in from other places
52
54
 
@@ -16,6 +16,8 @@ Above the cards, Today shows the day in two columns.
16
16
 
17
17
  Press **Prepare my day** to start the day. It runs from the brief to the delivery of each agent job, and you do not press anything else until a draft is ready for you. The page [Guide: Today and the proactive engine](proactive-engine.md) describes each step.
18
18
 
19
+ The bar at the top of the day is one line. It shows the greeting, the date and how many goals are unfinished. While **Prepare my day** runs, it also shows the four steps (Read, Sort, Agents work, You approve) as a small progress bar; press a step to see its detail. When nothing runs, it says when the day was last prepared. **Sources** and **Check now** are icon buttons in the bar.
20
+
19
21
  If the morning-brief session stops before it submits a brief, the Read step says so and gives the reason. **Prepare my day** is then ready again, so press it to try once more. If the session says that its folder is not there, open the repository again from its current folder first.
20
22
 
21
23
  Each agent job runs in its own session, so you can open any job and follow or steer it. The workspace types the job's prompt into the session when the agent is ready to read, and presses Enter for you. When you approve or revise a job and its session is still open, the workspace uses that same session again. Prepare again works the same way: when the morning-brief session is still open and has finished its read, or has printed nothing for two minutes, the workspace types the read into that session and runs it again.
@@ -6,17 +6,22 @@
6
6
  * be done to them. Pure: the server passes in the rows and the clock, so the rules are testable without a server.
7
7
  *
8
8
  * Next step, Hand to agent, a skill draft and an alignment are NOT automatic: the PM pressed those, and often talks on.
9
+ * Any other origin is automatic.
9
10
  */
10
11
  'use strict';
11
12
 
12
- const AUTOMATIC_EXACT = Object.freeze(['day-task', 'day-check', 'my-work-read', 'docs:sync', 'docs:check']);
13
- const AUTOMATIC_PREFIX = Object.freeze(['schedule:']);
13
+ // The rule is "if the PM did not trigger it, it is automatic", so the list names what the PM DID trigger and everything
14
+ // else with an origin is automatic. A new background job is then hidden and cleaned up without anyone remembering to list it.
15
+ // Manual: a session opened with no origin, a Next step or Hand to agent press, a skill draft, an alignment, and the Whisper
16
+ // and final-pass setup shells (the PM pressed those and watches them install).
17
+ const MANUAL_EXACT = Object.freeze(['next-step', 'plan-item', 'notes-whisper-setup', 'notes-final-setup']);
18
+ const MANUAL_PREFIX = Object.freeze(['skill-draft:', 'align:']);
14
19
 
15
- /** True when the workspace, not the PM, started a session with this origin. */
20
+ /** True when the PM did not trigger a session with this origin: a schedule, the day run, a doc sync, a note's passes, Jira. */
16
21
  function isAutomatic(origin) {
17
22
  const o = String(origin || '');
18
23
  if (!o) return false;
19
- return AUTOMATIC_EXACT.includes(o) || AUTOMATIC_PREFIX.some((p) => o.startsWith(p));
24
+ return !(MANUAL_EXACT.includes(o) || MANUAL_PREFIX.some((p) => o.startsWith(p)));
20
25
  }
21
26
 
22
27
  const DEFAULT_POLICY = Object.freeze({ finish: true, idleMinutes: 60 });
@@ -57,9 +62,10 @@ function decide(s, policy, now) {
57
62
  return age >= FINISH_GRACE_MS ? 'remove' : null;
58
63
  }
59
64
  if (!policy.idleMinutes) return null;
60
- // waiting on a person is not idle: the session is asking something
61
- if (s.shown === 'needs' || s.shown === 'blocked') return null;
65
+ // A call waiting for permission is a real ask. 'needs' (the agent's turn is over) is NOT: a finished agent always reads
66
+ // 'needs you', so exempting it would keep every finished job for ever. The idle clock still gives a late question time.
67
+ if (s.shown === 'blocked') return null;
62
68
  return age >= policy.idleMinutes * 60 * 1000 ? 'end' : null;
63
69
  }
64
70
 
65
- module.exports = { isAutomatic, normalizePolicy, decide, endedCleanly, DEFAULT_POLICY, FINISH_GRACE_MS, AUTOMATIC_EXACT, AUTOMATIC_PREFIX };
71
+ module.exports = { isAutomatic, normalizePolicy, decide, endedCleanly, DEFAULT_POLICY, FINISH_GRACE_MS, MANUAL_EXACT, MANUAL_PREFIX };
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pmos-workspace",
3
- "version": "0.87.0",
3
+ "version": "0.89.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pmos-workspace",
9
- "version": "0.87.0",
9
+ "version": "0.89.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.87.0",
3
+ "version": "0.89.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",
@@ -414,7 +414,7 @@ function readFrame() {
414
414
  }
415
415
  const fr = (() => {
416
416
  const s = readFrame();
417
- return { dest: FRAME_DESTS.includes(s.dest) ? s.dest : 'today', scope: s.scope === 'all' ? 'all' : 'repo', params: {}, dock: {}, dockMax: false, dockW: null, syncing: false, reg: null, gAt: 0, needs: 0 };
417
+ return { dest: FRAME_DESTS.includes(s.dest) ? s.dest : 'today', scope: s.scope === 'all' ? 'all' : 'repo', params: {}, dock: {}, dockMax: false, dockW: null, dockSize: null, syncing: false, reg: null, gAt: 0, needs: 0 };
418
418
  })();
419
419
  function writeFrame() {
420
420
  try { localStorage.setItem(FRAME_KEY, JSON.stringify({ dest: fr.dest, scope: fr.scope })); } catch { /* private mode: the default stands */ }
@@ -444,6 +444,34 @@ function toggleFocus() {
444
444
  if (fr.dockMax) fr.dock[fr.dest] = true;
445
445
  setState({});
446
446
  }
447
+ /* Dock size: set the dock to one of four preset sizes, or to focus mode.
448
+ * Normal: default width (no CSS override). Half: 50vw. Two-thirds: 66.667vw (the body spans the window).
449
+ * Focus: the dock takes the whole window (same as toggleFocus). */
450
+ const DOCK_SIZE_KEY = 'pmos.workspace.pm.dock-size';
451
+ const DOCK_SIZES = ['normal', 'half', 'two-thirds'];
452
+ function readDockSize() {
453
+ if (!FRAME) return null;
454
+ let v = null;
455
+ try { v = localStorage.getItem(DOCK_SIZE_KEY) || null; } catch { v = null; }
456
+ return v === 'half' || v === 'two-thirds' ? v : null;
457
+ }
458
+ function setDockSize(next) {
459
+ if (!FRAME) return;
460
+ if (next === 'whole') {
461
+ if (!fr.dockMax) toggleFocus();
462
+ return;
463
+ }
464
+ if (!DOCK_SIZES.includes(next)) return;
465
+ // A preset replaces a dragged pixel width, and leaving focus returns to the size chosen here.
466
+ fr.dockW = null;
467
+ try { localStorage.removeItem(DOCK_W_KEY); } catch { /* private mode */ }
468
+ fr.dockSize = next === 'normal' ? null : next;
469
+ try { if (fr.dockSize) localStorage.setItem(DOCK_SIZE_KEY, fr.dockSize); else localStorage.removeItem(DOCK_SIZE_KEY); } catch { /* private mode */ }
470
+ fr.dockMax = false;
471
+ fr.dock[fr.dest] = true;
472
+ paintDockWidth();
473
+ setState({});
474
+ }
447
475
  /* The dock's width, chosen with the grip (0.53.1). Kept in this browser like the dock's other per-window choices;
448
476
  * null is the default width. */
449
477
  const DOCK_W_KEY = 'pmos.workspace.pm.dock-width';
@@ -457,12 +485,21 @@ const dockWidth = {
457
485
  get: () => fr.dockW,
458
486
  set: (px) => {
459
487
  fr.dockW = Number.isFinite(px) && px > 0 ? Math.round(px) : null;
488
+ fr.dockSize = null; // dragging the grip, or a double-click, replaces a preset
460
489
  try { if (fr.dockW) localStorage.setItem(DOCK_W_KEY, String(fr.dockW)); else localStorage.removeItem(DOCK_W_KEY); } catch { /* private mode */ }
490
+ if (!fr.dockSize) try { localStorage.removeItem(DOCK_SIZE_KEY); } catch { /* private mode */ }
461
491
  paintDockWidth();
462
492
  },
463
493
  };
464
494
  function paintDockWidth() {
465
- const v = fr.dockW ? fr.dockW + 'px' : '';
495
+ let v = '';
496
+ if (fr.dockW) {
497
+ v = fr.dockW + 'px';
498
+ } else if (fr.dockSize === 'half') {
499
+ v = '50vw';
500
+ } else if (fr.dockSize === 'two-thirds') {
501
+ v = '66.667vw';
502
+ }
466
503
  if (bodyGrid.style.getPropertyValue('--fr-dock-w') !== v) {
467
504
  if (v) bodyGrid.style.setProperty('--fr-dock-w', v); else bodyGrid.style.removeProperty('--fr-dock-w');
468
505
  }
@@ -574,6 +611,7 @@ function setupFrame() {
574
611
  document.addEventListener('pointerdown', (e) => { fr.downAt = e.target; }, true);
575
612
  dockRail = createDockRail({ open: openDock, resize: dockWidth });
576
613
  fr.dockW = readDockW();
614
+ fr.dockSize = readDockSize();
577
615
  frDetail = el('div', { class: 'fr-detail', 'data-fr': 'detail' },
578
616
  el('div', { class: 'fr-detail__bar' }, iconButton({ icon: 'x', ariaLabel: FC.detail.close, onClick: closeDetail, attrs: { 'data-fr': 'detail-close' } })),
579
617
  hostDetail);
@@ -712,9 +750,9 @@ function frameKey(e) {
712
750
 
713
751
  /* What the suites and the phone chrome reach the frame by. */
714
752
  const frameApi = {
715
- on: true, go, toggleDock, foldDock, openDock, toggleFocus, dockWidth, chooseRepo, chooseAll, openPalette, repoMenu, openBranchesSheet, closeDetail, sync: runSync,
753
+ on: true, go, toggleDock, foldDock, openDock, toggleFocus, setDockSize, dockWidth, chooseRepo, chooseAll, openPalette, repoMenu, openBranchesSheet, closeDetail, sync: runSync,
716
754
  get unsynced() { return !!fr.unsynced; },
717
- get dest() { return fr.dest; }, get scope() { return fr.scope; }, get dockOpen() { return dockOpen(); }, get dockMax() { return !!fr.dockMax; },
755
+ get dest() { return fr.dest; }, get scope() { return fr.scope; }, get dockOpen() { return dockOpen(); }, get dockMax() { return !!fr.dockMax; }, get dockSize() { return fr.dockSize || 'normal'; },
718
756
  get detailOpen() { return !!state.col2; }, get syncing() { return fr.syncing; }, get needs() { return fr.needs || 0; },
719
757
  };
720
758