create-pmos 0.525.0 → 0.527.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 (78) hide show
  1. package/package.json +51 -56
  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/briefing-premium-board-test.js +1 -1
  7. package/workspace/changelog.json +50 -0
  8. package/workspace/ev-everywhere-test.js +2 -2
  9. package/workspace/guide/agent-capabilities.md +28 -2
  10. package/workspace/guide/board.md +14 -1
  11. package/workspace/guide/claude-proposes.md +3 -1
  12. package/workspace/guide/home.md +11 -5
  13. package/workspace/guide/idle-lab.md +2 -2
  14. package/workspace/guide/lifecycle.md +16 -3
  15. package/workspace/guide/reports-set-up.md +1 -0
  16. package/workspace/guide/today.md +11 -3
  17. package/workspace/lib/bg-queue.js +7 -3
  18. package/workspace/lib/board-premium.js +4 -1
  19. package/workspace/lib/briefing-premium-agent.js +9 -2
  20. package/workspace/lib/engine/boards.js +3 -2
  21. package/workspace/lib/headless.js +19 -4
  22. package/workspace/lib/idle-lab-engine.js +531 -0
  23. package/workspace/lib/initiative-chat.js +12 -2
  24. package/workspace/lib/initiative-cli.js +1 -1
  25. package/workspace/lib/initiative-premium.js +11 -3
  26. package/workspace/lib/lab-cli.js +220 -0
  27. package/workspace/lib/lifecycle-stage.js +17 -3
  28. package/workspace/lib/one-stage.js +131 -0
  29. package/workspace/lib/pmos-verbs.js +3 -0
  30. package/workspace/lib/proposals.js +5 -2
  31. package/workspace/lib/report-chat.js +31 -4
  32. package/workspace/lib/report-draft.js +1 -1
  33. package/workspace/lib/route-modules.js +1 -0
  34. package/workspace/lib/routes-board-premium.js +22 -0
  35. package/workspace/lib/routes-boards.js +61 -12
  36. package/workspace/lib/routes-briefing-premium.js +18 -1
  37. package/workspace/lib/routes-notes.js +1 -1
  38. package/workspace/lib/routes-your-move.js +64 -0
  39. package/workspace/lib/status-signal.js +123 -0
  40. package/workspace/lib/workflow-cli.js +3 -1
  41. package/workspace/lib/your-move.js +61 -0
  42. package/workspace/package-lock.json +2 -2
  43. package/workspace/package.json +1 -1
  44. package/workspace/public/pm/bd-acts.js +2 -1
  45. package/workspace/public/pm/bd-cols.js +8 -5
  46. package/workspace/public/pm/bd-prog-panel.js +3 -2
  47. package/workspace/public/pm/bd-span.js +2 -1
  48. package/workspace/public/pm/bd-views.js +8 -6
  49. package/workspace/public/pm/cozy-home.css +3 -0
  50. package/workspace/public/pm/cozy-today.css +151 -0
  51. package/workspace/public/pm/cozy.css +9 -0
  52. package/workspace/public/pm/good-morning.js +327 -0
  53. package/workspace/public/pm/grill-words.js +16 -1
  54. package/workspace/public/pm/home-app.js +187 -18
  55. package/workspace/public/pm/home-chat.js +6 -2
  56. package/workspace/public/pm/home-data.js +45 -13
  57. package/workspace/public/pm/home-day.js +66 -14
  58. package/workspace/public/pm/home-ini-model.js +1 -1
  59. package/workspace/public/pm/home-stage-card.js +7 -0
  60. package/workspace/public/pm/ini-tasks.js +2 -2
  61. package/workspace/public/pm/ini-view-model.js +10 -3
  62. package/workspace/public/pm/ini-view.css +9 -0
  63. package/workspace/public/pm/ini-view.js +19 -5
  64. package/workspace/public/pm/move-rank.js +155 -0
  65. package/workspace/public/pm/premium-board.js +7 -4
  66. package/workspace/public/pm/premium-initiative-head.js +39 -4
  67. package/workspace/public/pm/premium-report-tab.js +15 -3
  68. package/workspace/public/pm/report-flow.js +7 -1
  69. package/workspace/public/pm/stage-chip.js +44 -0
  70. package/workspace/run-tests.js +1 -0
  71. package/workspace/server.js +43 -1
  72. package/workspace/skills/idle-lab-prototype.skill +36 -0
  73. package/workspace/skills/idle-lab-scan.skill +32 -0
  74. package/workspace/test-lists/good-morning.json +9 -0
  75. package/workspace/test-lists/idle-lab-engine.json +9 -0
  76. package/workspace/test-lists/one-stage.json +5 -0
  77. package/workspace/test-lists/status-signal.json +5 -0
  78. package/workspace/test-lists/your-move.json +9 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-pmos",
3
- "version": "0.525.0",
3
+ "version": "0.527.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": {
@@ -32,6 +32,56 @@
32
32
  "audit"
33
33
  ],
34
34
  "pmosChanges": [
35
+ {
36
+ "v": "0.527.0",
37
+ "at": "2026-11-04T11:15:45.443Z",
38
+ "f": 4,
39
+ "prs": [
40
+ {
41
+ "n": 1237,
42
+ "t": "Chat refinements keep drafting the open report",
43
+ "s": "Chat refinements keep drafting the open report",
44
+ "k": "fix"
45
+ }
46
+ ]
47
+ },
48
+ {
49
+ "v": "0.526.0",
50
+ "at": "2026-11-04T10:37:51.951Z",
51
+ "f": 4,
52
+ "prs": [
53
+ {
54
+ "n": 1226,
55
+ "t": "Your move is one ranked list of every open ✦, with ages (workspace grill W3)",
56
+ "s": "Your move is one ranked list of every open ✦, with ages (workspace grill W3)",
57
+ "k": "new"
58
+ },
59
+ {
60
+ "n": 1225,
61
+ "t": "Status says what it knows — On track only from a read (workspace grill W2)",
62
+ "s": "Status says what it knows — On track only from a read (workspace grill W2)",
63
+ "k": "new"
64
+ },
65
+ {
66
+ "n": 1236,
67
+ "t": "Idle lab engine — prototypes and scans while the PM is away (idle lab build E)",
68
+ "s": "Idle lab engine — prototypes and scans while the PM is away (idle lab build E)",
69
+ "k": "new"
70
+ },
71
+ {
72
+ "n": 1230,
73
+ "t": "One true stage per initiative, read from the real workflow (workspace grill W1)",
74
+ "s": "One true stage per initiative, read from the real workflow (workspace grill W1)",
75
+ "k": "new"
76
+ },
77
+ {
78
+ "n": 1235,
79
+ "t": "Good morning is one page — the quarter first, then today, then time",
80
+ "s": "Good morning is one page — the quarter first, then today, then time",
81
+ "k": "new"
82
+ }
83
+ ]
84
+ },
35
85
  {
36
86
  "v": "0.525.0",
37
87
  "at": "2026-11-04T09:08:28.453Z",
@@ -363,61 +413,6 @@
363
413
  "d": "The record of what the decision guard checked went to a shared place. Now it stays in your own folder."
364
414
  }
365
415
  ]
366
- },
367
- {
368
- "v": "0.507.0",
369
- "at": "2026-11-03T12:20:44.537Z",
370
- "f": 4,
371
- "prs": [
372
- {
373
- "n": 1194,
374
- "t": "Rehearse a plan before you approve it (pmos rehearse)",
375
- "s": "Rehearse a plan before you approve it (pmos rehearse)",
376
- "k": "new",
377
- "h": "Rehearse a plan",
378
- "d": "Moving a date or cutting a task can quietly break a milestone. Now, before you approve a plan, Claude walks through the change and shows what it breaks or helps. Nothing changes until you say yes."
379
- }
380
- ]
381
- },
382
- {
383
- "v": "0.506.0",
384
- "at": "2026-11-03T12:14:24.342Z",
385
- "f": 4,
386
- "prs": [
387
- {
388
- "n": 1191,
389
- "t": "Ai-guard, one guard for every AI job",
390
- "s": "Ai-guard, one guard for every AI job",
391
- "k": "new",
392
- "h": "One switch for every AI job",
393
- "d": "Claude runs several jobs in the background, each on its own. Now one shared guard sits in front of all of them, so you can pause them all in a single step."
394
- },
395
- {
396
- "n": 1193,
397
- "t": "Decision memory, the grill's earlier-decision angle",
398
- "s": "Decision memory, the grill's earlier-decision angle",
399
- "k": "new",
400
- "h": "Earlier decisions come back",
401
- "d": "A grill could forget something you decided weeks ago. Now, when you grill something similar, Claude brings up what you decided before."
402
- },
403
- {
404
- "n": 1195,
405
- "t": "Ask PMOS about the past, with the record behind every answer",
406
- "s": "Ask PMOS about the past, with the record behind every answer",
407
- "k": "new",
408
- "h": "Ask PMOS about the past",
409
- "d": "You could not ask why a plan changed or what an earlier grill decided. Now you can, and every answer shows the record it came from, so you can check it.",
410
- "top": "Claude now remembers earlier decisions and answers questions about the past, with the record behind every answer."
411
- },
412
- {
413
- "n": 1192,
414
- "t": "What would I have missed, a weekly check for quiet drift",
415
- "s": "What would I have missed, a weekly check for quiet drift",
416
- "k": "new",
417
- "h": "A weekly check for quiet drift",
418
- "d": "Work can slide away from its goal without anyone noticing. Now, once a week, Claude looks and tells you what you would have missed, with the evidence."
419
- }
420
- ]
421
416
  }
422
417
  ]
423
418
  }
@@ -1,3 +1,3 @@
1
- kernel-source: pmos@b69d50bb
1
+ kernel-source: pmos@e3e4aeb7
2
2
  assembled: 2026-11-04
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-11-04T09:08:25Z"}
5
+ generated: {by: process:create-pmos, at: "2026-11-04T11:15:42Z"}
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-11-04T09:08:25Z"}
5
+ generated: {by: process:create-pmos, at: "2026-11-04T11:15:42Z"}
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-11-04T09:08:25Z"}
5
+ generated: {by: process:create-pmos, at: "2026-11-04T11:15:42Z"}
6
6
  status: stable
7
7
  ---
8
8
 
@@ -561,7 +561,7 @@ const IVREAD = () => {
561
561
  while (Date.now() - t0 < 90000) { const r = await fetch(BASE + '/api/board-premium'); j = await r.json(); if (j.state === 'ready' || j.state === 'error') break; if (r.status !== 202) break; await sleep(1500); }
562
562
  ok('E1 the real route answers "reading" first and then the board of this repository', !!j && j.ok && j.state === 'ready' && j.model.board.name.length > 0, JSON.stringify(j).slice(0, 200));
563
563
  const m = j && j.model;
564
- ok('E2 the real model\'s counts agree with each other: the header, the dock and the late and at-risk rows', !!m && m.dock.needs === m.initiatives.risk + m.initiatives.late && m.initiatives.on + m.initiatives.risk + m.initiatives.late + m.initiatives.hold === m.initiatives.open && m.initiatives.list.rows.slice(0, 5).every((r) => r.bad || m.initiatives.risk + m.initiatives.late === 0), m ? JSON.stringify(m.initiatives).slice(0, 200) : '');
564
+ ok('E2 the real model\'s counts agree with each other (no signal counted apart, workspace grill W2): the header, the dock and the late and at-risk rows', !!m && m.dock.needs === m.initiatives.risk + m.initiatives.late && m.initiatives.on + m.initiatives.risk + m.initiatives.late + m.initiatives.hold + (m.initiatives.none || 0) === m.initiatives.open && m.initiatives.list.rows.slice(0, 5).every((r) => r.bad || m.initiatives.risk + m.initiatives.late === 0), m ? JSON.stringify(m.initiatives).slice(0, 200) : '');
565
565
  ok('E3 the real model draws no sprint and no release without a source, and no demo data', !!m && !m.sprint.name && !m.release.at && !/demo/i.test(JSON.stringify({ h: m.header, s: m.sprint, r: m.release, d: m.docs, b: m.board })));
566
566
  ok('E3 the real model has the eight steps and a track on every row', !!m && m.steps.length === 8 && m.initiatives.list.rows.every((r) => r.track && typeof r.track.k === 'number'));
567
567
  const post = await fetch(BASE + '/api/board-premium', { method: 'POST', headers: await T.pressHeaders(BASE), body: '{}' }).then((r) => r.status);
@@ -1,4 +1,54 @@
1
1
  [
2
+ {
3
+ "v": "0.527.0",
4
+ "at": "2026-11-04T11:15:45.443Z",
5
+ "f": 4,
6
+ "prs": [
7
+ {
8
+ "n": 1237,
9
+ "t": "Chat refinements keep drafting the open report",
10
+ "s": "Chat refinements keep drafting the open report",
11
+ "k": "fix"
12
+ }
13
+ ]
14
+ },
15
+ {
16
+ "v": "0.526.0",
17
+ "at": "2026-11-04T10:37:51.951Z",
18
+ "f": 4,
19
+ "prs": [
20
+ {
21
+ "n": 1226,
22
+ "t": "Your move is one ranked list of every open ✦, with ages (workspace grill W3)",
23
+ "s": "Your move is one ranked list of every open ✦, with ages (workspace grill W3)",
24
+ "k": "new"
25
+ },
26
+ {
27
+ "n": 1225,
28
+ "t": "Status says what it knows — On track only from a read (workspace grill W2)",
29
+ "s": "Status says what it knows — On track only from a read (workspace grill W2)",
30
+ "k": "new"
31
+ },
32
+ {
33
+ "n": 1236,
34
+ "t": "Idle lab engine — prototypes and scans while the PM is away (idle lab build E)",
35
+ "s": "Idle lab engine — prototypes and scans while the PM is away (idle lab build E)",
36
+ "k": "new"
37
+ },
38
+ {
39
+ "n": 1230,
40
+ "t": "One true stage per initiative, read from the real workflow (workspace grill W1)",
41
+ "s": "One true stage per initiative, read from the real workflow (workspace grill W1)",
42
+ "k": "new"
43
+ },
44
+ {
45
+ "n": 1235,
46
+ "t": "Good morning is one page — the quarter first, then today, then time",
47
+ "s": "Good morning is one page — the quarter first, then today, then time",
48
+ "k": "new"
49
+ }
50
+ ]
51
+ },
2
52
  {
3
53
  "v": "0.525.0",
4
54
  "at": "2026-11-04T09:08:28.453Z",
@@ -118,10 +118,10 @@ const put = (root, rel, text) => { fs.mkdirSync(path.dirname(path.join(root, rel
118
118
  posts.length = 0;
119
119
  const nfy = (await json('GET', '/api/news-for-you?ws=' + encodeURIComponent(ws))).body || {};
120
120
  await pg.click('#hmList [data-hm-day]');
121
- const yd = await until(() => pg.evaluate(() => { const v = document.querySelector('#czViewer:not([hidden])'); if (!v || !v.querySelector('.yd')) return null; const f = v.querySelector('.czs-sec[data-czs-id="news"]');
121
+ const yd = await until(() => pg.evaluate(() => { const v = document.querySelector('#czViewer:not([hidden])'); if (!v || !v.querySelector('.yd')) return null; const f = v.querySelector('.gm-since');
122
122
  return { cal: !!v.querySelector('.cz-vw-hd [data-cz-vw-p="cal"]'), more: !!v.querySelector('.cz-vw-hd [data-cz-vw="more"]'), fold: !!f, rows: f ? f.querySelectorAll('[data-yd="news"]').length : 0, all: !!(f && f.querySelector('[data-yd="seen"]')) }; }), 20000);
123
123
  ok('N1 Good morning opens Your day, with the calendar as an icon in its header and no ⋯', !!yd && yd.cal && !yd.more, JSON.stringify(yd));
124
- ok('N1 New for you is a fold of Your day: the newest 3 at most, with ✓✓; nothing new draws no fold', !!yd && (nfy.count > 0 ? yd.fold && yd.rows === Math.min(3, nfy.count) && yd.all : !yd.fold), JSON.stringify({ yd, count: nfy.count }));
124
+ ok('N1 New for you is Good morning’s Since yesterday (nyt-ui GM2): the newest 3 at most, with ✓✓; nothing new draws no block', !!yd && (nfy.count > 0 ? yd.fold && yd.rows === Math.min(3, nfy.count) && yd.all : !yd.fold), JSON.stringify({ yd, count: nfy.count }));
125
125
  ok('N1 the place menu no longer opens a New for you page', await pg.evaluate(() => !document.querySelector('#czMenu [data-cz-news]')));
126
126
  const own = posts.filter((u) => u !== '/api/loose-props/rows');
127
127
  ok('N1 opening Your day posted nothing', own.length === 0, JSON.stringify(posts));
@@ -18,7 +18,7 @@ This page lists every job, so you know what runs and why. Each row links to the
18
18
  - **Few at a time.** A run shows at most 3 proposals. In a quiet week it shows nothing.
19
19
  - **Tested first.** A new version of a job shows nothing until its replay passes: its own known cases, and the guard's own cases (a made-up quote, a missing source, a path outside the repository, a write key). Each of those must be dropped.
20
20
  - **Your login, your connections.** A job runs as a background Claude session with your own login, model, profile and connectors. The workspace holds no password or token. `PMOS_HEADLESS=off` sends every job back to a terminal pane.
21
- - **One queue.** Every background job waits for its turn in one queue. Each minute the jobs go in this order: what is due at a set time (schedules, the morning brief and day check, the data refresh, report schedules), then the Initiative Driver, then the deep drafts (core documents, Your impact, metric drafts and reads, grill, proposals), then the idle work (the Horizon scan). What you ask for, such as Scan now or Prepare my day, starts at once and never waits in the queue.
21
+ - **One queue.** Every background job waits for its turn in one queue. Each minute the jobs go in this order: what is due at a set time (schedules, the morning brief and day check, the data refresh, report schedules), then the Initiative Driver, then the deep drafts (core documents, Your impact, metric drafts and reads, grill, proposals), then the idle work (the Horizon scan, then the idle lab). What you ask for, such as Scan now or Prepare my day, starts at once and never waits in the queue.
22
22
  - **One run for each initiative.** A background run never starts on an initiative that another background run is working on. It tries again a minute later.
23
23
  - **Idle work waits for you to step away.** The idle work starts only when you have not used PMOS for 30 minutes, or in the last hour of your working day if you never stepped away. It also waits when 3 background runs are live, inside quiet hours, and at the spending limit.
24
24
  - **Your hours.** Scheduled jobs run inside your working hours and never inside your quiet hours.
@@ -38,6 +38,7 @@ This page lists every job, so you know what runs and why. Each row links to the
38
38
  | [Expected outcomes](outcomes.md) | Once a day. At most 3 drafts a day for each product. | The PRD's success criteria, or the anchored key result | "Expected outcome" under the initiative's ✦. Today when a check is due. | `pmos ai off outcomes`. `PMOS_OUTCOMES=off` |
39
39
  | [Metric drafts](metric-drafts.md) | A product with no North Star, the first 14 days of a quarter, a program with no goal, and an initiative that gets a PRD | The last 8 weeks of real readings | The page of that product, program or initiative | `PMOS_METRIC_DRAFT=off` |
40
40
  | [Horizon scan](horizon-scan.md) | Daily by default, at most once a day, the first time you step away after 09:00. Scan now: at most 3 a day. | The public web for your kept watch list, and your repository | Report documents and proposals you approve | Cadence: Off. `PMOS_SCAN=off` |
41
+ | Idle lab | When you have not used PMOS for 30 minutes. One run at a time, at most 3 results each time you step away. | Prototypes: the initiative's record, PRD and earlier specimens. Scans: public pages only, read through `pmos lab page`, and no repository. | "Claude prototyped it", "Claude improved it" and "Claude scanned for it" under the initiative's ✦. A finding that fits no initiative: Intelligence › Lab. | `pmos ai off idle-lab` |
41
42
  | [What you might have missed](what-you-missed.md) | Each Monday, for the week before. It uses no model. | Records only: tasks, dates, initiatives and Jira status | One ✦ in Home's chat, at most 3 items | `pmos ai off drift`. `PMOS_DRIFT=off` |
42
43
  | [Your impact](your-impact.md) | Claude reads once a day. Home shows the week after it ends. | Slack, mail, Jira, your meeting notes, your documents and your key results | Adler's sprout on Home, and your impact log | Settings › General › Your impact. `PMOS_IMPACT=off` |
43
44
  | [Morning brief and day check](proactive-engine.md) | The morning brief at 06:45, once you schedule it. The day check every 60 minutes by default (30, 60 or off). | Calendar, Wispr Flow, Slack, Jira and Confluence are on by default. Mail, GitHub, Drive, Figma and websites are off by default. Mail: subjects and senders only. | Today | Each source is a setting. `pmos proactive settings` |
@@ -74,4 +75,29 @@ When you ask an agent session to do a piece of work, it can finish it, including
74
75
  - `pmos ai log [<job>]`: the last audit lines. Each dropped proposal shows why.
75
76
  - `pmos ai off [<job>]` and `pmos ai on [<job>]`: turn every AI job, or one job, off or on. `PMOS_AI=off` in the environment wins over the setting.
76
77
 
77
- The AI jobs are `past-repeat`, `decision-memory`, `outcomes`, `descriptions`, `drift`, `rehearse`, `next-prompt` and `sharpen-prompt`. The switch also covers each job in the queue by its queue name: `schedules`, `proactive`, `prepare`, `day`, `refresh`, `reports`, `driver`, `finder`, `coredocs`, `milestones`, `propose`, `grill`, `metric-drafts`, `impact`, `metric-reads`, `forecast`, `report-design`, `report-structure` and `scan`. `pmos ai off` with no job stops all of them. Git sync uses no model, so the switch does not stop it. A background run cannot change the switch.
78
+ The AI jobs are `past-repeat`, `decision-memory`, `outcomes`, `descriptions`, `drift`, `rehearse`, `next-prompt`, `sharpen-prompt` and `idle-lab`. The switch also covers each job in the queue by its queue name: `schedules`, `proactive`, `prepare`, `day`, `refresh`, `reports`, `driver`, `finder`, `coredocs`, `milestones`, `propose`, `grill`, `metric-drafts`, `impact`, `metric-reads`, `forecast`, `report-design`, `report-structure`, `scan` and `idle-lab`. `pmos ai off` with no job stops all of them. Git sync uses no model, so the switch does not stop it. A background run cannot change the switch.
79
+
80
+ ## The idle lab
81
+
82
+ When you step away, Claude does one of these for your initiatives, in this order:
83
+
84
+ 1. **Improve a prototype.** You dropped a round with a reason, or a grill or scan finding came in after the last round. Claude makes one new round of one specimen and says what changed. It never starts a new round while one waits for you.
85
+ 2. **Prototype a new idea.** An initiative in Triage or Discover whose specimen test says a description is not enough, and that has no prototype yet. Claude makes 2 or 3 different specimens.
86
+ 3. **Scan for something new.** A kept watch item that no scan read in 14 days, or an early initiative that nobody looked outside for.
87
+ 4. **Refresh an old scan.** A scan finding that is 14 days old. Claude keeps only what is new since then.
88
+
89
+ Before you see a result, PMOS proves it:
90
+
91
+ - Each specimen must open and render in a headless browser on this computer, with the network shut. With no headless browser, no prototype is shown.
92
+ - Each claim must quote what was read: a prototype quotes the initiative's own words at `path:line`, and a scan quotes a line of a page the run read.
93
+ - What is not proven is dropped, and the reason is logged. `pmos lab status` shows this month's runs, and `pmos ai log idle-lab` shows each drop.
94
+
95
+ Specimens stay in the runtime folder on this computer. Nothing goes into a product repository before your ✓. The idle lab's spend counts toward the same limit as the grill in Settings › Claude's checks.
96
+
97
+ The run uses only these commands:
98
+
99
+ - `pmos lab page <url>`: a scan run reads one public page. PMOS refuses a private or local address and keeps the page text, so a quote can be checked.
100
+ - `pmos lab file <group> --json -`: a run files one result. It waits until the run ends and the proof passes.
101
+ - `pmos lab status`: in a run, what it filed. Outside a run, this month's idle lab runs and spend.
102
+
103
+ Not proven yet: the compare view's Production and Design columns stay left out until PMOS shows on your computer that it can render production from the product repository and read the design.
@@ -2,13 +2,26 @@
2
2
  type: Guide
3
3
  title: "pmos board: the items of every initiative"
4
4
  description: "How pmos board prints the items of every initiative in four columns from a terminal, and where the workspace shows initiatives and their tasks."
5
- timestamp: 2026-11-03
5
+ timestamp: 2026-11-04
6
6
  ---
7
7
 
8
8
  # pmos board: the items of every initiative
9
9
 
10
10
  The **Board** place of the workspace shows initiatives, not items: every initiative of the products you pick, by stage, with programs, a timeline and a list. [Programs on the Board](programs.md) explains it. An initiative's tasks show under it in Home's list and in the initiative's page in the Viewer.
11
11
 
12
+ ## What an initiative's status means
13
+
14
+ The status of an initiative tells only what PMOS read. If PMOS read nothing, the status is **No signal**.
15
+
16
+ - **On track**: a Jira read from the last 14 days shows no ticket past its due date or sprint end, or the plan items have dates that are still ahead.
17
+ - **Late**: a date passed. This is the planned finish of the plan items, or a milestone.
18
+ - **At risk**: a read found a problem, or you confirmed a risk. A problem is an overdue item, a blocked or late Jira ticket, or work that is idle. To confirm a risk, press ✓ on Claude's "Mark … at risk" suggestion for a program milestone.
19
+ - **No signal** (grey): PMOS read nothing that tells how the work goes. No signal never counts as on track.
20
+
21
+ To read Jira for the initiatives with no signal, open **Needs a look** and press **✦ Read Jira now**.
22
+
23
+ The weekly update in Home follows the same rules. Claude drafts it only when something moved this week or a risk is open. The update starts with **Before you send**: the risks that Claude found and you did not confirm. Press ✓ to confirm all of them. The mail does not include these risks. The initiatives with no signal are under **Not read**.
24
+
12
25
  The command below prints the items themselves, from a terminal.
13
26
 
14
27
  ## From a terminal
@@ -2,7 +2,7 @@
2
2
  type: Guide
3
3
  title: "Claude proposes: the one ✦ on each initiative"
4
4
  description: "What Claude finds when you add an initiative that already lives elsewhere, the new dates, done and tidy names it proposes for late tasks, the clean-up of the work that is not in an initiative, how you keep or drop them in the Viewer, Undo, the pmos propose commands, and how Claude grills an initiative by itself (pmos grill)."
5
- timestamp: 2026-11-03
5
+ timestamp: 2026-11-04
6
6
  ---
7
7
 
8
8
  # Claude proposes: the one ✦ on each initiative
@@ -71,6 +71,8 @@ You can also start one any time: in the initiative's chat, write "grill this" (o
71
71
 
72
72
  Each finding is a proposal in the group "Claude grilled it", under the same ✦. Each row shows the mark and the name of the angle that found it: Premortem, Red team, Voices, Reframe, Scope, What-if or Earlier decision. Voices are simulated from your stored research and meeting notes, only with 3 sources or more, and carry the tag "Simulated, not evidence". A What-if carries "Simulated, not the plan". Each finding ends with one next step. ✓ adds that step as a task. ✎ lets you write the step in your own words first; ✓ then adds your words. ✕ drops the finding. A finding never rides on the header's ✓. Above the findings, one quiet line names the angles Claude chose, when and why it ran, and what the run cost. Three findings show at a time; "+N smaller findings" and its ⋯ show the rest. On a phone the findings are cards side by side: swipe for the next. With no findings, nothing shows.
73
73
 
74
+ The initiative's header shows its two strongest findings, each with its angle and day, and a ✦ with the count that opens all of them. Every finding, including the ones behind "+N smaller findings", is also ranked in Your move in [Your day](today.md).
75
+
74
76
  When Claude grilled an initiative this week and left findings, Home says so in one line under "Good morning", and its ✦ opens the findings. When it grilled more than one, the line names the newest and says how many more, for example "I grilled Inline login and 5 more this week: 21 findings." The others' findings are under each initiative's own ✦. When a key result will miss its goal, or drifted to at risk, Intelligence's ✦ line names it and the initiative Claude grilled for it; its ✦ opens those findings.
75
77
 
76
78
  ### Earlier decision: Claude remembers what you decided
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  type: Guide
3
3
  title: "Home: your list, the chat and the Viewer"
4
- description: "Home: the list grouped by product, the Viewer, the place menu, Your day and its New for you fold, Search, and the initiative, meeting, report, Board and Insights features designed after it."
4
+ description: "Home: the list grouped by product, the Viewer, the place menu, Your day as the Good morning page, Search, and the initiative, meeting, report, Board and Insights features designed after it."
5
5
  timestamp: 2026-11-04
6
6
  ---
7
7
 
@@ -15,13 +15,19 @@ Home, the Board, Insights and the Viewer use one typeface, Plus Jakarta Sans, wi
15
15
 
16
16
  - The top line has **Good morning**, the product picker, the filter icon (on a desktop) and **New**. When the list is narrow, the icons move to a second line, so the greeting is never cut. The line under it says how many items are yours, for example "4 are yours".
17
17
  - Press **Good morning** to open **Your day** in the Viewer. This is the same on a computer and on a phone. To open the calendar, press the calendar icon in Your day's header. A program's milestone changes that wait on you are the first line of the program's card.
18
- - A blue dot after **Good morning** means that something is new for you. It is in the **New for you** fold of Your day.
19
- - Your day shows the date, the meetings of the day, **Your move** (the items that wait on you, with their count), **New for you**, and **This week** (the Weekly review).
18
+ - A blue dot after **Good morning** means that something is new for you. It is under **Since yesterday** in Your day.
19
+ - Your day is the **Good morning** page. It shows the date and greeting, then the quarter, then two columns. On a computer it opens wide.
20
+ - **The quarter.** One bar with all goals of the products Home reads: reached, on track, still reading, at risk and will miss, each with its count. A black tick marks your line, for example "line 18". One sentence says how many goals are on track or better, and one line says why. **By product** opens Intelligence. With no goals, there is no bar. With no line, there is no tick, and Claude can propose a line with ✦: press ✓ to keep it, or ✕ to not set one this quarter. A product that could not be read is named and is not counted.
21
+ - **The left column.** Claude's lead, with one line of what Claude did for you. **Your call**: the one decision that needs you, and what Yes does when that is known. **Your move**: the top 3 items of the ranked list, each with the goal it moves and its due day (orange only when late). Then one line: **N more in Your move**, **N drafted for you** and **N can wait**. Press one to open its fold under the page. **Since yesterday**: up to 3 new items, then **New for you · N** to see all of them. ✓✓ marks all of them seen.
22
+ - **The right column.** Today's agenda, not to scale: a line at the time now, each meeting with its prep note, ✦ picks in free time, and "3 h free" for a long free stretch. The meeting where your call is decided has an orange edge. Under it, **Notes since yesterday**: meeting notes with their main lines, and recordings with no initiative. When Claude finds the initiative a recording belongs to, press the link icon to link it.
23
+ - **In the hour before a meeting**, the meeting's prep leads: what to agree, the points, and the record icon. The quarter shows only when it moved since the morning. Past meetings fade, with their notes under them.
24
+ - **On a phone or in a narrow Viewer**, the page is one column: the quarter, the lead, your call, Your move, the agenda, then Since yesterday.
25
+ - Under the page, the folds stay: the rest of **Your move**, **Drafted for you**, **Can wait**, **Now** (on a phone) and **This week** (the Weekly review).
20
26
  - **Drafted for you** lists what Claude drafted today and the weekly update. Press a row to open it: a finished draft opens what Claude wrote, and the weekly update opens the update.
21
27
  - Your day does not show the meeting on now or the next meeting. Home's meeting line shows them. On a phone, the **Now** card does not show while a meeting is in progress.
22
28
  - When a conversation in Mentions waits on your answer, Your day names the oldest one under its first line: who asked, where, how many more wait, and what they asked. Press its icon to open that conversation in Mentions.
23
- - **New for you** shows only when something is new: tasks given to you, and work that finished while you were away. It shows the 3 newest items, then **N more**. Press an item to open it in the Viewer. Press **N more** to see all of them. Press ✓✓ in the fold's header to mark all of them seen. An item leaves when you open it or mark it seen. Mentions are not here: **Mentions** shows each one once.
24
- - After 17:00, Your day is the end of the day. It shows what moved today, then a **Tomorrow** fold with tomorrow's meetings. When tomorrow has no meetings, there is no Tomorrow fold.
29
+ - **Since yesterday** shows only when something is new: tasks given to you, and work that finished while you were away. It shows the 3 newest items, then **New for you · N**. Press an item to open it in the Viewer. Press **New for you · N** to see all of them. Press ✓✓ in its header to mark all of them seen. An item leaves when you open it or mark it seen. Mentions are not here: **Mentions** shows each one once.
30
+ - After 17:00, Your day is the end of the day. The quarter says how it moved since the morning. **Today made possible** lists what moved today, and **Still yours** lists what is left. The right column shows tomorrow's meetings.
25
31
  - A **program** comes first, above the products. Its header shows the program icon, its name, the next milestone (◆ and the date) and how many of its initiatives are ready for that milestone. Inside it, the program's own tasks come first, then its initiatives under a small heading for each product. Each initiative shows once. Press the program's name to open the program in the Viewer. The chat then talks about the program. Press the chevron to close or open the group.
26
32
  - The **product picker** is the chip on the top line. It shows the tiles of the ticked products, overlapped, with **+N** after 3, or **All** when every product is ticked. It shows even with one product. A git worktree of a repository is not a product: it does not appear in the list. Its repository stands for it. Hold the pointer on it to read the names. Press it to open the list: **All products**, then each product with its tile, its name and one line (for example "3 initiatives · 1 of 2 goals"), then **Add a product**. Press a product to tick or untick it; the list stays open, and the last ticked product stays ticked. The pick applies when the list closes (Escape, a press outside, or the chip again). On a phone the list is a sheet at the bottom. The pick is one saved setting (`ws.briefing.include`) that Home, the Board and Insights share, so a product that you untick on one page goes from all three. With **All** ticked, a product that you add later is ticked too. The **Product** row in Settings opens the same list.
27
33
  - A program with no open initiative yet still shows, with "No initiatives yet" or "No open initiatives". A program shows only when it holds work of a ticked product. Home reads again when you come back to its tab, so a program made on the Board shows at once.
@@ -14,12 +14,12 @@ When you step away from PMOS, Claude can use the idle time to make prototypes an
14
14
  A result for one initiative waits under that initiative's blue ✦, in one of three groups:
15
15
 
16
16
  - **Claude prototyped it.** A first round of 2 or 3 different ways to do it, as thumbnails. Press a thumbnail to open it in the compare view.
17
- - **Claude improved it.** A new round of one prototype. It says what changed, usually one thing, and where the change came from, for example a meeting note.
17
+ - **Claude improved it.** A new round of one prototype. It says what changed, usually one thing, and where the change came from: your ✕ reason, a grill finding or a scan finding.
18
18
  - **Claude scanned for it.** A finding to attach as evidence, with the pages Claude read.
19
19
 
20
20
  ✓ keeps it as one of the initiative's documents. ✕ asks why first. Claude learns from the reason, and you can leave it empty. Press ✕ again to drop it. Undo in the toast takes a ✓ back.
21
21
 
22
- A result that fits no initiative waits in **Intelligence › Lab**. One kind proposes a new initiative from a scan finding: ✓ starts it on its board.
22
+ A result that fits no initiative waits in **Intelligence › Lab**. ✓ keeps the finding.
23
23
 
24
24
  ## The compare view
25
25
 
@@ -2,7 +2,7 @@
2
2
  type: Guide
3
3
  title: "The lifecycle: six stages and two milestones"
4
4
  description: "The eight steps every initiative goes through, what each step makes, which steps each lane uses, and how the workspace knows where an initiative is."
5
- timestamp: 2026-11-02
5
+ timestamp: 2026-11-04
6
6
  ---
7
7
 
8
8
  # The lifecycle: six stages and two milestones
@@ -35,9 +35,22 @@ A step that a lane does not use shows as skipped.
35
35
  | content | Discover, Plan, Build, Deliver |
36
36
  | prototype | Triage, Discover, Evaluate |
37
37
 
38
- ## How the workspace knows the step
38
+ ## One stage on every page
39
39
 
40
- Nobody sets the step. The workspace reads it from the records:
40
+ Every page shows the same stage chip: the Board row, the Timeline, the initiative page, Documents, the chat and the program page. The workspace reads the stage from these sources, in this order:
41
+
42
+ 1. Your word. A lifecycle line (Completed shows Shipped, On hold, Cancelled), or a move on the Board that is newer than the last Jira read.
43
+ 2. The linked Jira epic. Its status names the stage (Design Discovery shows Discover, In progress shows Build, Done shows Deliver, or Monitor after your sign-off). If the epic status names no stage, its tickets do: some started shows Build, all done shows Deliver.
44
+ 3. Plan items with dates. A spec-agreed initiative with dated plan items shows Prioritize.
45
+ 4. The spec files, as described below.
46
+
47
+ Hover the chip to see what set the stage and when Jira was last read. With no word from you and no Jira, the spec files decide.
48
+
49
+ The spec ladder (PRD, rubric, contract, review) shows as the gate line under the stage card. The CI Initiative Gate still reads the spec files. A task group shows a tick only when all of its tasks are done.
50
+
51
+ ## How the spec files give the step
52
+
53
+ When no word and no Jira set the stage, the workspace reads it from the records:
41
54
 
42
55
  - Triage, Discover, Plan, Build and Evaluate come from the workflow engine (`node scripts/workflow-state.js`): the PRD, the rubric and contract, the build, and the evaluation.
43
56
  - An idea or a hypothesis on the initiative also counts for Discover.
@@ -58,6 +58,7 @@ Each cycle is a document: Draft or Issued, with its output, its copy state and w
58
58
  - PMOS writes to Google or to Confluence only when you press **Create the copy** or **Update the copy**.
59
59
  - A download happens only when you press a download item.
60
60
  - You issue a report. An issued report is frozen.
61
+ - When a report has an open draft, keep talking in the chat. Each change you ask for, such as a date, a wording or a look, goes to that draft by itself. You do not press anything. The draft updates beside the chat, and Claude says in one line what it passed. An issued report does not change. Change its template for the next cycle.
61
62
 
62
63
  ## Send a dashboard as a report
63
64
 
@@ -2,15 +2,23 @@
2
2
  type: Guide
3
3
  title: "What waits for you, and pmos today"
4
4
  description: "Where the workspace shows what waits for you (Your day in Home) and how pmos today prints the same kind of list from a terminal."
5
- timestamp: 2026-11-03
5
+ timestamp: 2026-11-04
6
6
  ---
7
7
 
8
8
  # What waits for you, and pmos today
9
9
 
10
10
  In the workspace, what waits for you is in **Your day** on Home. Press **Good morning** at the top of Home's list to open it in the Viewer.
11
11
 
12
- - **Your move** lists the items that wait on you, with their count.
13
- - **New for you** shows what is new since you last looked: tasks given to you, and work that finished while you were away.
12
+ - **Your move** is one ranked list of everything Claude proposes (✦) that waits on you, with its count. Each row shows how long it has waited. The order is:
13
+ 1. Outcome checks past their date. When Claude could read the Jira tickets, it drafts Keep or Iterate, and ✓ records that verdict. When it could not read enough, ✎ opens Intelligence. Claude never drafts Stop.
14
+ 2. Grill findings on work that shipped or is late.
15
+ 3. Milestone changes per program, as one row. ✓ confirms all of them, and ✎ lets you pick.
16
+ 4. Grill findings on work ahead.
17
+ 5. The first key result with no reading, and the source it waits on.
18
+ 6. The other proposals, one row per initiative. A row older than two weeks folds under **Older than two weeks**.
19
+ - In Your move, findings of one kind on one initiative are one row, with the others counted ("2 more findings like it"). ✕ drops a finding.
20
+ - **New for you** shows what is new since you last looked. When there are two or more tasks given to you, they are one row. Work that finished while you were away is also listed.
21
+ - When you have no meeting today, Your day shows no meeting line and no offer to prepare.
14
22
  - **Drafted for you** lists what Claude drafted today.
15
23
  - **This week** opens the weekly review.
16
24
 
@@ -17,7 +17,8 @@
17
17
  * 3 driver the Initiative Driver
18
18
  * 4 deep the deep drafts: core documents, Your impact, metric drafts and reads, proposals, grill, the report finder, the
19
19
  * report design and structure checks, milestone reviews, the forecast
20
- * 5 idle the idle lab and the slow reads: the Horizon scan (its daily run waits for the first idle stretch of the day)
20
+ * 5 idle the idle lab and the slow reads: the Horizon scan (its daily run waits for the first idle stretch of the day), then the idle
21
+ * lab (lib/idle-lab-engine.js: prototypes and scans for the PM's initiatives)
21
22
  *
22
23
  * THE CHECKS (made once per tick, for every job):
23
24
  * off switch lib/ai-guard.js off(job): PMOS_AI=off, PMOS_AI_<JOB>=off, Settings ai.on / ai.off (`pmos ai off [job]`). A job with
@@ -77,6 +78,7 @@ const JOBS = Object.freeze([
77
78
  { id: 'report-design', step: 4, label: 'Report design' },
78
79
  { id: 'report-structure', step: 4, label: 'Report structure' },
79
80
  { id: 'scan', step: 5, label: 'Horizon scan' },
81
+ { id: 'idle-lab', step: 5, label: 'Idle lab' },
80
82
  ]);
81
83
  const BY_ID = new Map(JOBS.map((j) => [j.id, j]));
82
84
  const LAST_HOUR_MS = 60 * 60 * 1000;
@@ -122,18 +124,20 @@ function holderOf(sessions, initiative, { except = null } = {}) {
122
124
  }
123
125
  return null;
124
126
  }
125
- /** The initiative an origin names: drive:initiative:<id>, grill:<ws>:<id>:<run>, propose:<ws>:<id>:<run>. */
127
+ /** The initiative an origin names: drive:initiative:<id>, grill:<ws>:<id>:<run>, propose:<ws>:<id>:<run>, idle-lab-proto|idle-lab-scan:<ws>:<id|->:<run>. */
126
128
  function initiativeOfOrigin(origin) {
127
129
  const p = String(origin || '').split(':');
128
130
  if (p[0] === 'drive' && p[1] === 'initiative') return p.slice(2).join(':') || null;
129
131
  if ((p[0] === 'grill' || p[0] === 'propose') && p.length >= 4) return p[2] || null;
132
+ if ((p[0] === 'idle-lab-proto' || p[0] === 'idle-lab-scan') && p.length >= 4) return p[2] && p[2] !== '-' ? p[2] : null;
130
133
  return null;
131
134
  }
132
135
 
133
136
  /* origin prefix → queue job. Origins not listed (a refresh source, a schedule, next-step) keep their prefix as the job name. */
134
137
  const ORIGIN_JOB = Object.freeze({ drive: 'driver', grill: 'grill', propose: 'propose', 'metric-read': 'metric-reads', 'metric-draft': 'metric-drafts',
135
138
  impact: 'impact', coredoc: 'coredocs', 'core-docs': 'coredocs', 'scan-web': 'scan', 'scan-repo': 'scan', 'scan-suggest': 'scan',
136
- 'scan-research-web': 'scan', 'scan-research-repo': 'scan', 'report-find': 'finder', 'ms-review': 'milestones', 'next-step': 'prepare' });
139
+ 'scan-research-web': 'scan', 'scan-research-repo': 'scan', 'report-find': 'finder', 'ms-review': 'milestones', 'next-step': 'prepare',
140
+ 'idle-lab-proto': 'idle-lab', 'idle-lab-scan': 'idle-lab' });
137
141
  function jobOfOrigin(origin) {
138
142
  const o = String(origin || '');
139
143
  if (!o || /^chat:/.test(o)) return null;
@@ -244,8 +244,11 @@ function wfFields(r) {
244
244
  const wfMap = mapFields(r.wfMap);
245
245
  const wfSwitch = r.wfSwitch && typeof r.wfSwitch.batch === 'string' ? { batch: r.wfSwitch.batch, at: r.wfSwitch.at || null, from: r.wfSwitch.from || null } : null;
246
246
  return { wf, wfStage: stageFields(r.wfStage), wfStages: Array.isArray(r.wfStages) ? r.wfStages.map(stageFields).filter(Boolean).slice(0, 40) : null, ended,
247
- ...(wfMap ? { wfMap } : {}), ...(wfSwitch ? { wfSwitch } : {}) };
247
+ ...(wfMap ? { wfMap } : {}), ...(wfSwitch ? { wfSwitch } : {}), ...(r.oneStage ? { oneStage: oneFields(r.oneStage) } : {}) };
248
248
  }
249
+ /** W1: the one stage as the routes give it (lib/one-stage.js stageOf): the chip (public/pm/stage-chip.js) draws it on every surface. */
250
+ const oneFields = (o) => (o && typeof o === 'object' && o.stage ? { stage: String(o.stage), step: String(o.step || ''), label: clip(o.label || '', 40),
251
+ setBy: ['pm', 'jira', 'plan', 'files'].includes(o.setBy) ? o.setBy : 'files', source: clip(o.source || '', 120), readAt: o.readAt || null } : null);
249
252
  /** A stage map as the row carries it: { old key: stage key }, short strings only (a key is never cut: a cut key matches nothing), at most 60
250
253
  * keys; null when empty. */
251
254
  function mapFields(m) {
@@ -16,6 +16,7 @@ const crypto = require('crypto');
16
16
  const headless = require('./headless.js');
17
17
  const Goal = require('./goal-chat.js');
18
18
  const ReportChat = require('./report-chat.js');
19
+ const REPORT_KEYS = new Set(); // chats that carried the report rule: their later messages carry the short reminder (item 9, batch 27)
19
20
  const ProgramChat = require('./program-chat.js');
20
21
  const PastChat = require('./past-chat.js');
21
22
  const WorkflowChat = require('./workflow-chat.js');
@@ -200,7 +201,7 @@ function openingFor(row, text, extra = []) {
200
201
  }
201
202
  /** sessions: { alive(id) → bool, spawn(spec) → session row, type(id, text) → bool (Enter after, `/` left as typed) }.
202
203
  * iniInfo: () → lib/initiative-chat.js infoOf answer | null, read only when an init: chat needs it (its opening, a message about a stage). */
203
- function chat({ runtimeDir, key, text, model, context, sessions, wsId, iniInfo, now = new Date() }) {
204
+ function chat({ runtimeDir, key, text, model, context, sessions, wsId, iniInfo, reportInfo, now = new Date() }) {
204
205
  if (!runtimeDir) return fail(409, 'this workspace has no state folder to keep the conversation in');
205
206
  if (!KEY_RE.test(String(key || ''))) return fail(400, 'key must name a row');
206
207
  const q = String(text == null ? '' : text).trim();
@@ -210,6 +211,8 @@ function chat({ runtimeDir, key, text, model, context, sessions, wsId, iniInfo,
210
211
  const live = chatSessionOf(runtimeDir, key);
211
212
  const alive = !!(live && sessions.alive(live));
212
213
  let infoRead = false, infoVal = null;
214
+ let linked = null;
215
+ const reportOf = () => { try { return typeof reportInfo === 'function' ? reportInfo() : null; } catch { return null; } };
213
216
  const info = () => { if (!infoRead) { infoRead = true; try { infoVal = typeof iniInfo === 'function' ? iniInfo() : null; } catch { infoVal = null; } } return infoVal; };
214
217
  // slice 4 (PRD L2): an ended chat resumes its own Claude Code conversation when the file for it is still there
215
218
  const prev = alive ? null : chatRecordOf(runtimeDir, key);
@@ -227,7 +230,11 @@ function chat({ runtimeDir, key, text, model, context, sessions, wsId, iniInfo,
227
230
  // bug 2026-10-25: in a live initiative chat, a message about a stage, a workflow or a move carries the initiative's workflow and stage, read now
228
231
  else if (alive && /^init:/.test(key) && IniChat.isAsk(q)) msg = IniChat.wrap(q, info(), iniId(key));
229
232
  // premium-reports-from-chat R1: a message about a report, a chart or a dashboard carries the pmos-report rule (lib/report-chat.js)
230
- else if (ReportChat.isAsk(q)) msg = ReportChat.wrap(q);
233
+ // Wawan, 2026-11-04: a chat with a set-up report open beside it carries that report on every message, so feedback in any words reaches the draft (lib/report-chat.js)
234
+ else if (!q.startsWith('/') && (linked = reportOf())) { msg = ReportChat.wrap(q, linked); }
235
+ else if (ReportChat.isAsk(q)) { msg = ReportChat.wrap(q); REPORT_KEYS.add(runtimeDir + '|' + key); }
236
+ // a refinement later in a chat that carried the report rule (it need not say "report") is still about that report's draft
237
+ else if (REPORT_KEYS.has(runtimeDir + '|' + key) && !q.startsWith('/')) msg = ReportChat.remind(q);
231
238
  // bug 2026-10-24: a message about a program carries the program rule (lib/program-chat.js), so the chat names the real press
232
239
  else if (ProgramChat.isAsk(q)) msg = ProgramChat.wrap(q);
233
240
  const extra = goal ? { goal } : {};
@@ -164,8 +164,9 @@ const TOOLS = Object.freeze({
164
164
 
165
165
  // on hold and cancelled come from a D70 lifecycle line (the PM stopped it); done is the end of the workflow. Only the first
166
166
  // three are ACTIVE: an initiative on hold, done or cancelled has no health and is not counted as open (c3-initiative-lifecycle).
167
- const STATUSES = Object.freeze(['on track', 'at risk', 'late', 'on hold', 'done', 'cancelled']);
168
- const ACTIVE = Object.freeze(['on track', 'at risk', 'late']);
167
+ // 'no signal' (workspace grill W2): open, but nothing PMOS read says how it is going. It is active, and never counted as on track
168
+ const STATUSES = Object.freeze(['on track', 'at risk', 'late', 'no signal', 'on hold', 'done', 'cancelled']);
169
+ const ACTIVE = Object.freeze(['on track', 'at risk', 'late', 'no signal']);
169
170
  const isActive = (r) => !!r && ACTIVE.includes(r.status);
170
171
  const VIEWS = Object.freeze(['now', 'mine', 'risk', 'noowner', 'all']);
171
172
  /** What a Horizon scan filed with an ask (initiative horizon-scan), cut to its shape; null for any other ask. */