create-pmos 0.166.0 → 0.168.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.
Files changed (52) hide show
  1. package/package.json +1 -1
  2. package/payload/KIT-VERSION +1 -1
  3. package/payload/planning/evals/eval-calibration.md +1 -1
  4. package/payload/planning/okrs/health-budgets.md +1 -1
  5. package/payload/state/discoveries.md +1 -1
  6. package/workspace/extension/README.md +33 -0
  7. package/workspace/extension/address.js +20 -0
  8. package/workspace/extension/content.js +76 -0
  9. package/workspace/extension/icons/icon-128.png +0 -0
  10. package/workspace/extension/icons/icon-16.png +0 -0
  11. package/workspace/extension/icons/icon-32.png +0 -0
  12. package/workspace/extension/icons/icon-48.png +0 -0
  13. package/workspace/extension/manifest.json +15 -0
  14. package/workspace/extension/options.css +33 -0
  15. package/workspace/extension/options.html +26 -0
  16. package/workspace/extension/options.js +24 -0
  17. package/workspace/extension/panel.css +21 -0
  18. package/workspace/extension/panel.html +14 -0
  19. package/workspace/extension/panel.js +67 -0
  20. package/workspace/guide/console-calendar.md +14 -0
  21. package/workspace/guide/index.md +1 -0
  22. package/workspace/guide/initiative-driver.md +79 -0
  23. package/workspace/guide/validate-with-typesafe.md +1 -1
  24. package/workspace/lib/driver-cli.js +207 -0
  25. package/workspace/lib/driver-decide.js +118 -0
  26. package/workspace/lib/driver-facts.js +113 -0
  27. package/workspace/lib/driver-needs.js +107 -0
  28. package/workspace/lib/driver-run.js +187 -0
  29. package/workspace/lib/driver.js +408 -0
  30. package/workspace/lib/extension-cli.js +39 -0
  31. package/workspace/lib/headless.js +13 -2
  32. package/workspace/lib/meeting-focus.js +48 -1
  33. package/workspace/lib/needs-providers.js +1 -0
  34. package/workspace/lib/pmos-verbs.js +6 -0
  35. package/workspace/lib/route-modules.js +1 -0
  36. package/workspace/lib/routes-boards.js +11 -1
  37. package/workspace/lib/routes-driver.js +135 -0
  38. package/workspace/lib/routes-meeting-focus.js +17 -1
  39. package/workspace/lib/typesafe-settings.js +15 -1
  40. package/workspace/package-lock.json +2 -2
  41. package/workspace/package.json +1 -1
  42. package/workspace/public/pm/c3-demo-focus.js +12 -2
  43. package/workspace/public/pm/c3-demo.js +1 -1
  44. package/workspace/public/pm/c3-focus.css +31 -0
  45. package/workspace/public/pm/c3-focus.js +122 -21
  46. package/workspace/public/pm/c3-settings.js +12 -2
  47. package/workspace/public/pm/c3-shell.js +1 -1
  48. package/workspace/server.js +32 -1
  49. package/workspace/skills/initiative-drive.skill +50 -0
  50. package/workspace/skills/work-unblock.skill +42 -0
  51. package/workspace/test-files.json +4 -0
  52. package/workspace/test-lists/c3-meeting-focus.json +13 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-pmos",
3
- "version": "0.166.0",
3
+ "version": "0.168.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@4ae0213
1
+ kernel-source: pmos@21b6322
2
2
  assembled: 2026-10-12
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-12T13:44:56Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-12T14:26:40Z"}
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-12T13:44:56Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-12T14:26:40Z"}
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-12T13:44:56Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-12T14:26:40Z"}
6
6
  status: stable
7
7
  ---
8
8
 
@@ -0,0 +1,33 @@
1
+ ---
2
+ type: Guide
3
+ title: "PMOS for Google Meet: the Chrome extension"
4
+ timestamp: 2026-10-12
5
+ ---
6
+
7
+ # PMOS for Google Meet
8
+
9
+ A small panel over a Google Meet call. It shows the PMOS meeting companion: the topic the room is on, the time left, what is being said, the notes from PMOS and a line to ask PMOS quietly. Only you see it. The extension has no build step and no remote code. It runs only on `https://meet.google.com/*`, reads the address and the title of the Meet tab, and talks only to the PMOS on your own computer.
10
+
11
+ ## Load it in Chrome
12
+
13
+ 1. Open `chrome://extensions`.
14
+ 2. Turn on **Developer mode** (top right).
15
+ 3. Press **Load unpacked** and pick this folder. `pmos extension` prints its path.
16
+
17
+ If PMOS does not run on `http://localhost:7433`, open the extension's **Options** and set the address that `pmos` printed. A Meet tab that is already open picks up the new address when you reload it.
18
+
19
+ ## Use it
20
+
21
+ Open a Meet call, for example `meet.google.com/abc-defg-hij`. The panel shows at the bottom right of the call. It finds the meeting on your PMOS calendar by the Meet link in the invite, then by the title of the tab, then by the meeting that is under way now. If none fits, the panel says so and lists today's meetings to pick from. **Fold** shrinks the panel to a pill, and the same button opens it again. **Open focus in PMOS** opens the full Meeting focus page in a new tab. When PMOS does not answer, the panel says "PMOS is not running at <address>" and links to the options.
22
+
23
+ ## Files
24
+
25
+ | File | What it is |
26
+ |---|---|
27
+ | `manifest.json` | Manifest V3. Permission: `storage` only. Content script on `https://meet.google.com/*`. |
28
+ | `content.js` | Adds the panel on a call page (`/abc-defg-hij`) and removes it when you leave. |
29
+ | `panel.html`, `panel.js`, `panel.css` | The page inside the panel. It checks that PMOS answers, then frames PMOS's compact Meeting focus page. |
30
+ | `options.html`, `options.js`, `options.css`, `address.js` | The one setting: where PMOS is. |
31
+ | `icons/` | The PMOS icon at four sizes. |
32
+
33
+ The extension ships in the workspace package. Publishing it in the Chrome Web Store is not part of it yet.
@@ -0,0 +1,20 @@
1
+ /* address.js — where PMOS is. Shared by panel.js and options.js (classic script; no build step).
2
+ * The address is kept in chrome.storage.sync under `pmosAddress`. The default is the workspace's default port.
3
+ * normalizeAddress('localhost:7500') → 'http://localhost:7500'; anything that is not an http or https address → ''. */
4
+ (function () {
5
+ 'use strict';
6
+ var DEFAULT_ADDRESS = 'http://localhost:7433';
7
+ var KEY = 'pmosAddress';
8
+ function normalizeAddress(text) {
9
+ var t = String(text == null ? '' : text).trim();
10
+ if (!t) return '';
11
+ if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(t)) t = 'http://' + t;
12
+ var u;
13
+ try { u = new URL(t); } catch (e) { return ''; }
14
+ if (u.protocol !== 'http:' && u.protocol !== 'https:') return '';
15
+ return u.origin;
16
+ }
17
+ var api = { DEFAULT_ADDRESS: DEFAULT_ADDRESS, KEY: KEY, normalizeAddress: normalizeAddress };
18
+ if (typeof module !== 'undefined' && module.exports) module.exports = api;
19
+ else window.PMOS_ADDRESS = api;
20
+ })();
@@ -0,0 +1,76 @@
1
+ /* content.js — the PMOS panel over a Google Meet call. Runs only on https://meet.google.com/* (manifest.json).
2
+ *
3
+ * On a call path (/abc-defg-hij) it puts a fixed host element at the bottom right of the page. Its shadow root holds one iframe: panel.html of this
4
+ * extension, with the Meet code and the tab's title in the address. When the panel folds, the page inside it sends { pmosFocus: 'fold' } (and 'open'
5
+ * when it unfolds) and the host shrinks to a pill. On any other Meet page (the home page, a lobby list) there is no panel.
6
+ *
7
+ * Meet is a single-page app, so the address is watched: it is read again every second and on popstate. The only things read from the page are
8
+ * the address and the title. Nothing is sent from here. No call audio or captions are read. */
9
+ (function () {
10
+ 'use strict';
11
+ var CALL_PATH = /^\/[a-z]{3}-[a-z]{4}-[a-z]{3}$/;
12
+ var HOST_ID = 'pmos-meet-panel';
13
+ var OPEN = { width: 360, height: 520 };
14
+ var PILL_HEIGHT = 44;
15
+ var TITLE_WAIT_MS = 3000; // Meet sets a title like "Meet - <name>" a moment after the call opens
16
+
17
+ /** The Meet code of a path, or '' when the path is not a call. */
18
+ function callCode(pathname) { return CALL_PATH.test(String(pathname || '')) ? String(pathname).slice(1) : ''; }
19
+ /** The panel's address: panel.html with the Meet code and the tab title. */
20
+ function panelSrc(base, code, title) { return base + '?meet=' + encodeURIComponent(code) + '&title=' + encodeURIComponent(title || ''); }
21
+
22
+ var api = { CALL_PATH: CALL_PATH, callCode: callCode, panelSrc: panelSrc };
23
+ if (typeof module !== 'undefined' && module.exports) { module.exports = api; return; }
24
+ if (typeof document === 'undefined' || typeof chrome === 'undefined' || !chrome.runtime || !chrome.runtime.getURL) return;
25
+ if (window.top !== window) return;
26
+
27
+ var CSS = [
28
+ ':host { position: fixed; right: 16px; bottom: 16px; z-index: 2147483647; width: ' + OPEN.width + 'px; height: ' + OPEN.height + 'px; max-width: calc(100vw - 32px); max-height: calc(100vh - 32px); }',
29
+ ':host([data-folded]) { height: ' + PILL_HEIGHT + 'px; }',
30
+ 'iframe { display: block; width: 100%; height: 100%; border: 1px solid rgba(128, 128, 128, 0.35); border-radius: 12px; background: Canvas; color-scheme: light dark; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3); }',
31
+ ':host([data-folded]) iframe { border-radius: 22px; }',
32
+ ].join('\n');
33
+
34
+ var mounted = null; // { code, host, frame }
35
+ var seenAt = 0; // when this call path was first seen
36
+
37
+ function unmount() {
38
+ if (mounted && mounted.host.parentNode) mounted.host.parentNode.removeChild(mounted.host);
39
+ mounted = null;
40
+ }
41
+ function mount(code) {
42
+ var host = document.createElement('div');
43
+ host.id = HOST_ID;
44
+ var root = host.attachShadow({ mode: 'closed' });
45
+ try { var sheet = new CSSStyleSheet(); sheet.replaceSync(CSS); root.adoptedStyleSheets = [sheet]; }
46
+ catch (e) { var st = document.createElement('style'); st.textContent = CSS; root.appendChild(st); }
47
+ var frame = document.createElement('iframe');
48
+ frame.title = 'PMOS meeting helper';
49
+ frame.src = panelSrc(chrome.runtime.getURL('panel.html'), code, document.title);
50
+ root.appendChild(frame);
51
+ document.documentElement.appendChild(host);
52
+ mounted = { code: code, host: host, frame: frame };
53
+ }
54
+
55
+ window.addEventListener('message', function (e) {
56
+ if (!mounted || e.source !== mounted.frame.contentWindow) return;
57
+ if (e.origin !== chrome.runtime.getURL('').replace(/\/$/, '')) return; // the panel's own origin, chrome-extension://<id>
58
+ var m = e.data && e.data.pmosFocus;
59
+ if (m === 'fold') mounted.host.setAttribute('data-folded', '');
60
+ else if (m === 'open') mounted.host.removeAttribute('data-folded');
61
+ });
62
+
63
+ function check() {
64
+ var code = callCode(location.pathname);
65
+ if (!code) { seenAt = 0; unmount(); return; }
66
+ if (mounted && mounted.code === code) return;
67
+ if (mounted) unmount();
68
+ if (!seenAt) seenAt = Date.now();
69
+ var titled = document.title && !/^\s*(Google )?Meet\s*$/i.test(document.title);
70
+ if (titled || Date.now() - seenAt >= TITLE_WAIT_MS) mount(code);
71
+ }
72
+ window.addEventListener('popstate', check);
73
+ window.addEventListener('hashchange', check);
74
+ setInterval(check, 1000);
75
+ check();
76
+ })();
@@ -0,0 +1,15 @@
1
+ {
2
+ "manifest_version": 3,
3
+ "name": "PMOS for Google Meet",
4
+ "version": "0.1.0",
5
+ "description": "Shows your PMOS meeting companion in a small panel over a Google Meet call. Only you see it. It talks only to the PMOS on your own computer.",
6
+ "icons": { "16": "icons/icon-16.png", "32": "icons/icon-32.png", "48": "icons/icon-48.png", "128": "icons/icon-128.png" },
7
+ "permissions": ["storage"],
8
+ "content_scripts": [
9
+ { "matches": ["https://meet.google.com/*"], "js": ["content.js"], "run_at": "document_idle" }
10
+ ],
11
+ "web_accessible_resources": [
12
+ { "resources": ["panel.html"], "matches": ["https://meet.google.com/*"] }
13
+ ],
14
+ "options_ui": { "page": "options.html", "open_in_tab": true }
15
+ }
@@ -0,0 +1,33 @@
1
+ /* options.css — the options page. PMOS does not serve this file, so the colours are PMOS's tokens by value. */
2
+ :root {
3
+ color-scheme: light dark;
4
+ --background: oklch(1 0 0);
5
+ --foreground: oklch(0.148 0.004 228.8);
6
+ --muted-foreground: oklch(0.48 0.021 213.5);
7
+ --border: oklch(0.925 0.005 214.3);
8
+ --accent: oklch(0.488 0.243 264.376);
9
+ --destructive: oklch(0.577 0.245 27.325);
10
+ }
11
+ @media (prefers-color-scheme: dark) {
12
+ :root {
13
+ --background: oklch(0.148 0.004 228.8);
14
+ --foreground: oklch(0.987 0.002 197.1);
15
+ --muted-foreground: oklch(0.723 0.014 214.4);
16
+ --border: oklch(1 0 0 / 10%);
17
+ --accent: oklch(0.623 0.214 259.815);
18
+ --destructive: oklch(0.704 0.191 22.216);
19
+ }
20
+ }
21
+ body { margin: 0; background: var(--background); color: var(--foreground); font: 14px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif; }
22
+ main { max-width: 560px; margin: 0 auto; padding: 32px 16px; }
23
+ h1 { margin: 0 0 8px; font-size: 20px; font-weight: 600; }
24
+ .lede { margin: 0 0 24px; color: var(--muted-foreground); }
25
+ code { font: 12px ui-monospace, SFMono-Regular, Menlo, monospace; }
26
+ label { display: block; margin-bottom: 6px; font-weight: 600; }
27
+ .row { display: flex; gap: 8px; }
28
+ input { flex: 1; min-width: 0; height: 32px; padding: 0 12px; border: 1px solid var(--border); border-radius: 6px; background: var(--background); color: var(--foreground); font: inherit; }
29
+ input:focus-visible, button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
30
+ button { height: 32px; padding: 0 16px; border: 0; border-radius: 6px; background: var(--foreground); color: var(--background); font: inherit; font-weight: 600; cursor: pointer; }
31
+ .status { min-height: 21px; margin: 8px 0 0; color: var(--muted-foreground); }
32
+ .status.bad { color: var(--destructive); }
33
+ .fine { margin: 24px 0 0; color: var(--muted-foreground); font-size: 12px; }
@@ -0,0 +1,26 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>PMOS for Google Meet: options</title>
7
+ <link rel="stylesheet" href="options.css">
8
+ </head>
9
+ <body>
10
+ <main>
11
+ <h1>PMOS for Google Meet</h1>
12
+ <p class="lede">The panel over a Meet call is a page of PMOS on your own computer. Type the address where your PMOS runs. The default is <code>http://localhost:7433</code>, the port the workspace starts on. If <code>pmos</code> printed another port, use that one.</p>
13
+ <form id="form">
14
+ <label for="addr">PMOS address</label>
15
+ <div class="row">
16
+ <input id="addr" name="addr" type="text" inputmode="url" autocomplete="off" spellcheck="false" placeholder="http://localhost:7433">
17
+ <button type="submit">Save</button>
18
+ </div>
19
+ <p id="status" class="status" role="status" aria-live="polite"></p>
20
+ </form>
21
+ <p class="fine">The extension runs only on meet.google.com. It reads the address and the title of the Meet tab, and it talks only to this PMOS address. It does not read the call's audio or captions.</p>
22
+ </main>
23
+ <script src="address.js"></script>
24
+ <script src="options.js"></script>
25
+ </body>
26
+ </html>
@@ -0,0 +1,24 @@
1
+ /* options.js — the one setting: where PMOS is (chrome.storage.sync, key pmosAddress). */
2
+ (function () {
3
+ 'use strict';
4
+ var A = window.PMOS_ADDRESS;
5
+ var input = document.getElementById('addr');
6
+ var status = document.getElementById('status');
7
+ var say = function (t, bad) { status.textContent = t; status.className = 'status' + (bad ? ' bad' : ''); };
8
+
9
+ try {
10
+ var o = {}; o[A.KEY] = A.DEFAULT_ADDRESS;
11
+ chrome.storage.sync.get(o, function (v) { input.value = A.normalizeAddress(v && v[A.KEY]) || A.DEFAULT_ADDRESS; });
12
+ } catch (e) { input.value = A.DEFAULT_ADDRESS; }
13
+
14
+ document.getElementById('form').addEventListener('submit', function (e) {
15
+ e.preventDefault();
16
+ var addr = A.normalizeAddress(input.value) || (input.value.trim() === '' ? A.DEFAULT_ADDRESS : '');
17
+ if (!addr) { say('That is not an address. Use a form like http://localhost:7433.', true); return; }
18
+ var o = {}; o[A.KEY] = addr;
19
+ chrome.storage.sync.set(o, function () {
20
+ input.value = addr;
21
+ say('Saved. A Meet tab that is open picks it up when you reload the tab.');
22
+ });
23
+ });
24
+ })();
@@ -0,0 +1,21 @@
1
+ /* panel.css — the panel's own page, shown only while PMOS is being looked for. PMOS does not serve this file, so the colours are PMOS's tokens by value. */
2
+ :root {
3
+ color-scheme: light dark;
4
+ --background: oklch(1 0 0);
5
+ --foreground: oklch(0.148 0.004 228.8);
6
+ --muted-foreground: oklch(0.48 0.021 213.5);
7
+ --accent: oklch(0.488 0.243 264.376);
8
+ }
9
+ @media (prefers-color-scheme: dark) {
10
+ :root {
11
+ --background: oklch(0.148 0.004 228.8);
12
+ --foreground: oklch(0.987 0.002 197.1);
13
+ --muted-foreground: oklch(0.723 0.014 214.4);
14
+ --accent: oklch(0.623 0.214 259.815);
15
+ }
16
+ }
17
+ html, body { height: 100%; margin: 0; }
18
+ body { background: var(--background); color: var(--foreground); font: 14px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif; }
19
+ .msg { margin: 0; padding: 16px; color: var(--muted-foreground); }
20
+ .msg a { color: var(--accent); text-underline-offset: 2px; }
21
+ iframe { display: block; width: 100%; height: 100%; border: 0; background: var(--background); }
@@ -0,0 +1,14 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>PMOS meeting helper</title>
7
+ <link rel="stylesheet" href="panel.css">
8
+ </head>
9
+ <body>
10
+ <main id="msg" class="msg" role="status" aria-live="polite">Looking for PMOS.</main>
11
+ <script src="address.js"></script>
12
+ <script src="panel.js"></script>
13
+ </body>
14
+ </html>
@@ -0,0 +1,67 @@
1
+ /* panel.js — the page the content script frames over a Meet call (panel.html).
2
+ * It reads the PMOS address from chrome.storage.sync (pmosAddress, default http://localhost:7433) and asks whether anything answers there.
3
+ * When PMOS answers, the page becomes one frame: ${address}/c3.html?focus=now&pop=1&meet=…&mtitle=… (PMOS's compact meeting focus). When it does not,
4
+ * the page says where it looked and links to the options. Nothing else is fetched. The frame's { pmosFocus } message (fold or open, no data) is passed
5
+ * up to the content script. */
6
+ (function () {
7
+ 'use strict';
8
+ var A = window.PMOS_ADDRESS;
9
+ var CHECK_MS = 4000; // how long to wait for PMOS to answer
10
+ var RETRY_MS = 5000; // looked again while it does not answer, so a PMOS started later fills the panel
11
+ var MEET_ORIGIN = 'https://meet.google.com';
12
+ var params = new URLSearchParams(location.search);
13
+ var meet = params.get('meet') || '';
14
+ var title = params.get('title') || '';
15
+ var msg = document.getElementById('msg');
16
+ var frame = null;
17
+ var frameOrigin = '';
18
+ var timer = null;
19
+
20
+ function readAddress(done) {
21
+ var fallback = A.DEFAULT_ADDRESS;
22
+ try {
23
+ var o = {}; o[A.KEY] = fallback;
24
+ chrome.storage.sync.get(o, function (v) { done(A.normalizeAddress(v && v[A.KEY]) || fallback); });
25
+ } catch (e) { done(fallback); }
26
+ }
27
+ /** Resolves when something answers at the address (an opaque no-cors reply is enough), rejects when nothing listens. */
28
+ function answers(addr) {
29
+ var ctl = typeof AbortController === 'function' ? new AbortController() : null;
30
+ var t = ctl ? setTimeout(function () { ctl.abort(); }, CHECK_MS) : null;
31
+ return fetch(addr + '/c3.html', { mode: 'no-cors', cache: 'no-store', signal: ctl ? ctl.signal : undefined })
32
+ .then(function (r) { if (t) clearTimeout(t); return r; }, function (e) { if (t) clearTimeout(t); throw e; });
33
+ }
34
+ function showMissing(addr) {
35
+ msg.textContent = '';
36
+ msg.className = 'msg';
37
+ msg.appendChild(document.createTextNode('PMOS is not running at ' + addr + '. '));
38
+ var a = document.createElement('a');
39
+ a.href = '#';
40
+ a.textContent = 'Set the address';
41
+ a.addEventListener('click', function (e) { e.preventDefault(); try { chrome.runtime.openOptionsPage(); } catch (x) { /* no options page */ } });
42
+ msg.appendChild(a);
43
+ msg.appendChild(document.createTextNode('.'));
44
+ }
45
+ function showFrame(addr) {
46
+ var q = new URLSearchParams({ focus: 'now', pop: '1', meet: meet, mtitle: title });
47
+ frame = document.createElement('iframe');
48
+ frame.title = 'PMOS meeting helper';
49
+ frame.src = addr + '/c3.html?' + q.toString();
50
+ frameOrigin = new URL(addr).origin;
51
+ msg.remove();
52
+ document.body.appendChild(frame);
53
+ }
54
+ function start() {
55
+ readAddress(function (addr) {
56
+ answers(addr).then(function () { showFrame(addr); }, function () { showMissing(addr); timer = setTimeout(start, RETRY_MS); });
57
+ });
58
+ }
59
+
60
+ window.addEventListener('message', function (e) {
61
+ if (!frame || e.source !== frame.contentWindow || e.origin !== frameOrigin) return;
62
+ var m = e.data && e.data.pmosFocus;
63
+ if (m !== 'fold' && m !== 'open') return;
64
+ if (window.parent !== window) window.parent.postMessage({ pmosFocus: m }, MEET_ORIGIN);
65
+ });
66
+ start();
67
+ })();
@@ -137,6 +137,20 @@ To fix the topic, press **Topics**. Press **Not this topic** and pick the topic
137
137
 
138
138
  The companion starts only while the meeting is recorded. It reads the new lines of the transcript at most every 40 seconds. It writes the sentence, the chips, the topic and the notes. If the answer is not usable, PMOS drops it and shows nothing. To stop the companion, set `PMOS_FOCUS_AGENT` to `off`. The page then shows the transcript and a quiet line, and it adds no notes. In the last 5 minutes, and after the meeting, the page shows the captured items under the material. You confirm them in the note after the meeting. Nothing goes to other people.
139
139
 
140
+ ### Meeting focus over Google Meet
141
+
142
+ The PMOS Chrome extension shows a small panel over a Google Meet call, so you do not need to switch to the PMOS tab. The panel is the Meeting focus page in a compact form. It has a head with the topic the room is on (or "Starts in" before the start) and the time left, the **What's being said** line, the conversation with PMOS, and **Ask PMOS quietly**. Only you see it. It is not part of the screen you share. The extension reads the address and the title of the Meet tab only. It does not read the call's audio or captions, and it talks only to the PMOS on your computer.
143
+
144
+ To add the extension:
145
+
146
+ 1. Run `pmos extension`. It prints the folder of the extension and these steps.
147
+ 2. In Chrome, open `chrome://extensions` and turn on **Developer mode**.
148
+ 3. Press **Load unpacked** and pick the folder that `pmos extension` printed.
149
+
150
+ PMOS must run while you use the panel. The extension looks for PMOS at `http://localhost:7433`. If `pmos` started on another port, open the extension's **Options**, type that address and press **Save**, then reload the Meet tab.
151
+
152
+ To use it, open a Meet call. The panel shows at the bottom right of the call, and not on the Meet home page. PMOS finds the meeting on your calendar by the Meet link in the invite. If no invite has that link, it uses the title of the tab, and then the meeting that is under way now (from 10 minutes before the start to 10 minutes after the end). If none fits, the panel says "No meeting right now" and lists today's meetings. Press the one this call is. **Fold** shrinks the panel to a pill that shows the head only, and the same button opens it again. **Open focus in PMOS** opens the full Meeting focus page in a new tab, with the material, the topics and the captured items. If PMOS does not answer, the panel says "PMOS is not running at" and the address, with a link to the Options.
153
+
140
154
  ## Free time
141
155
 
142
156
  Press **Free time** to show the free blocks. A free block is a gap inside your working hours that is long enough to use. On Day and Week, each block is a dashed purple window on the timeline, with its length, for example "1.5 h focus". The side column lists the blocks, up to 8, with the suggestion for each. On Month, Quarter and Year the list says that free time shows in the Day and Week views, and the chip still shows the number of blocks.
@@ -61,6 +61,7 @@ On a desktop the workspace is one frame. [Layout](layout.md) explains each part.
61
61
  - [Find knowledge without reading whole files](knowledge-graph.md): what `pmos kg` finds, its commands, how to read its numbers, and its limits.
62
62
  - [Initiatives: the Roadmap, the List and the initiative sheet](initiatives-screen.md): how to read and order the initiatives of a repository, open the sheet, make a new initiative, and review the cycle.
63
63
  - [Initiatives and the Roadmap: Now, Next and Later](initiatives-roadmap.md): what day one shows, how an agent proposes a move, and `pmos initiative` and `pmos measure` from a terminal.
64
+ - [The Initiative Driver: drafts the agent files for you](initiative-driver.md): when the driver runs, the unblock sweep, the limits, Keep, Send back and Drop, the board modes, and `pmos driver`.
64
65
  - [PM-only acts: what only you can do](pm-only-acts.md): which acts are yours, how the workspace checks that a press comes from your page or your terminal, and what to do when one is refused.
65
66
  - [PM-only lines: how the workspace checks your acts](pm-only-lines.md): how each act is recorded, what the Today card "A PM-only line is not attested" means, and the grants for a bulk lifecycle change and a Release.
66
67
  - [Breakdown › Timeline](timeline.md): an initiative's items on a time axis, split by stage, with milestones, today, and the at-risk flag when an item starts before its predecessor ends.
@@ -0,0 +1,79 @@
1
+ ---
2
+ type: Guide
3
+ title: "The Initiative Driver: drafts the agent files for you"
4
+ description: "What the Initiative Driver does when an initiative is created or moved, what stops it, how it files proposals you keep, send back or drop, the board modes, and the pmos driver commands."
5
+ timestamp: 2026-10-12
6
+ ---
7
+
8
+ # The Initiative Driver: drafts the agent files for you
9
+
10
+ The Initiative Driver is a short background agent run for one initiative or one Work task. It reads the record, decides the next useful step, files the result as a **proposal**, and ends. It never decides for you. You keep a proposal, send it back with a note, or drop it.
11
+
12
+ ## When a run starts
13
+
14
+ A run starts for one of four reasons:
15
+
16
+ - **Created.** An initiative is created from the page, from Triage (accept), or with `pmos initiative new`. The run starts within one tick of the server (about one minute).
17
+ - **Moved.** You move the initiative to another stage on the board.
18
+ - **Answered.** You send a proposal back with a note.
19
+ - **Stuck.** The unblock sweep finds a blocker the agent can work on.
20
+
21
+ An initiative that existed before you first ran this version does not start a run. Only events after the first scan count.
22
+
23
+ ## The unblock sweep
24
+
25
+ The sweep reads every active initiative and every open Work task. It uses no model. It runs every 30 minutes, inside the working hours of the daytime check (09:00 to 18:00 by default) and outside your quiet hours.
26
+
27
+ | Where | What it finds |
28
+ | --- | --- |
29
+ | You are the blocker | An open proposal is more than one day old. No run starts. The row comes back in Needs you once a day. |
30
+ | Discover | No kept problem statement, or no kept evidence. |
31
+ | Plan | No PRD, no done-criteria, or no anchor. |
32
+ | Do | No Jira ticket moved for the stall threshold, or a ticket is flagged blocked. |
33
+ | Learn | The check-by date passed with no outcome check, or a kill condition tripped. |
34
+ | Any stage | Nothing changed for the stall threshold (5 days by default). |
35
+ | Work task | Overdue, waiting on someone for 3 days or more, untouched for 7 days, or no next step. |
36
+
37
+ The sweep starts a run for a blocker the agent can work on, at most once per blocker per day. A dormant initiative starts no run.
38
+
39
+ ## The limits
40
+
41
+ - At most 3 runs per initiative per day, and 1 per Work task per day.
42
+ - At most 3 runs at the same time. The others wait.
43
+ - A run that fails or times out counts against the day. Its reason is in the ledger.
44
+ - A paused initiative or Work task starts no run.
45
+ - `PMOS_DRIVER=off` in the environment of the server starts none.
46
+
47
+ ## What the agent can and cannot do
48
+
49
+ The agent reads, and it files proposals with `pmos driver propose`. It has no Write or Edit tool, no `git push`, and no tool that sends anything. Every claim in a proposal carries a pointer: a Jira key, a link or a path. A follow-up to a person is draft text only. You send it from your own session.
50
+
51
+ In Discover, the agent writes the problem statement, the evidence, the opportunity sizing, the riskiest assumption and the stakeholders. When you have kept the first three, it proposes the move to Plan. In the other stages, it names the blocker and proposes what would clear it.
52
+
53
+ ## Keep, send back, drop
54
+
55
+ Open proposals appear in Needs you as one row for each initiative or task: "Agent has 3 drafts on the initiative".
56
+
57
+ - **Keep** writes the record as you. A problem statement goes into the problem record of the initiative. A move goes through the board move, with its warnings. A Work next step goes into the steps of the task. Other kinds are kept as your decision to rely on them.
58
+ - **Send back** needs a note. The note starts a new run. It counts as a correction.
59
+ - **Drop** closes the proposal. It counts as a correction.
60
+
61
+ The agent cannot keep, send back, drop, pause or change the mode. Those acts need your page or your terminal.
62
+
63
+ ## Board modes
64
+
65
+ - **Approve** is the default. The agent runs and drafts, and Needs you shows the rows.
66
+ - **Shadow.** The agent runs and drafts, and raises no Needs you rows.
67
+ - **Off.** No run starts.
68
+
69
+ ## The commands
70
+
71
+ The agent uses `pmos driver propose` to file a proposal and `pmos driver status` to read what exists. The other commands are yours, at an interactive terminal:
72
+
73
+ - `pmos driver keep <proposal>` asks you to type the proposal id. A kept move is pressed on the page.
74
+ - `pmos driver return <proposal> --note "<what to change>"` sends it back.
75
+ - `pmos driver drop <proposal>` drops it.
76
+ - `pmos driver pause <initiative|work> <id>` and `pmos driver resume <initiative|work> <id>` stop and restart the runs for one target.
77
+ - `pmos driver mode <off|shadow|approve> [--board <id>]` sets the board mode. Without `--board` it sets every board.
78
+
79
+ The ledger is `driver.jsonl` in the state folder of the repository (`pmos/state`, `state` or `.pmos`). Each action adds one line. Nothing is changed or removed.
@@ -112,7 +112,7 @@ The workspace reads the TypeSafe key only on this machine. After you save the ke
112
112
  If you have no TypeSafe key, a model on your computer can answer the same questions. Two models are supported. Both use the same interface as Jev.
113
113
 
114
114
  - **Winnow**: in a `winnow-inference` folder, run `python3 scripts/serve.py`. It uses port 8091. Winnow-12B needs about 16 GB of memory.
115
- - **Laya**: run `pip install "laya[serve]"`, then `laya-serve`. It uses port 8000. Laya is small and runs on a laptop.
115
+ - **Laya**: in Settings, press **Set up Laya**. A terminal named Set up Laya opens with the commands typed in. Read them and press Enter. The first time, they install Laya into the PMOS data folder. Then Laya starts and uses port 8000. Laya is small and runs on a laptop. Laya stops when you close that terminal or restart the computer. Press **Set up Laya** again to start it.
116
116
 
117
117
  1. Start the model.
118
118
  2. Open Settings, then **Privacy and data**. Under **Local model if Jev is missing**, press **Winnow** or **Laya**.