create-pmos 0.514.0 → 0.516.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 +35 -95
- package/payload/KIT-VERSION +2 -2
- package/payload/planning/evals/eval-calibration.md +1 -1
- package/payload/planning/okrs/health-budgets.md +1 -1
- package/payload/state/discoveries.md +1 -1
- package/payload/state/log.md +1 -1
- package/workspace/briefing-premium-calendar-test.js +6 -3
- package/workspace/briefing-premium-insights-test.js +12 -12
- package/workspace/briefing-premium-metrics-test.js +1 -1
- package/workspace/changelog.json +34 -0
- package/workspace/fixtures/premium-viewer-fixtures.js +4 -2
- package/workspace/guide/agent-capabilities.md +73 -0
- package/workspace/guide/index.md +4 -1
- package/workspace/guide/intelligence-okr-goals.md +39 -0
- package/workspace/guide/reports-needs-you.md +34 -0
- package/workspace/lib/board-metrics-premium.js +4 -1
- package/workspace/lib/insights-goals.js +3 -1
- package/workspace/lib/insights-premium.js +53 -7
- package/workspace/lib/okr-goals.js +200 -0
- package/workspace/lib/report-acts.js +189 -0
- package/workspace/lib/routes-insights-premium.js +44 -5
- package/workspace/lib/routes-ready-reports.js +8 -1
- package/workspace/lib/routes-report-setups.js +21 -1
- package/workspace/package-lock.json +2 -2
- package/workspace/package.json +1 -1
- package/workspace/premium-vw-meet-test.js +9 -5
- package/workspace/public/pm/cozy-insights.css +60 -1
- package/workspace/public/pm/home-app.js +3 -1
- package/workspace/public/pm/premium-insights.js +269 -36
- package/workspace/public/premium-intelligence.html +1 -1
- package/workspace/test-files.json +1 -0
- package/workspace/test-lists/reports-needs-you.json +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.516.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,40 @@
|
|
|
32
32
|
"audit"
|
|
33
33
|
],
|
|
34
34
|
"pmosChanges": [
|
|
35
|
+
{
|
|
36
|
+
"v": "0.516.0",
|
|
37
|
+
"at": "2026-11-04T00:00:07.601Z",
|
|
38
|
+
"f": 4,
|
|
39
|
+
"prs": [
|
|
40
|
+
{
|
|
41
|
+
"n": 1209,
|
|
42
|
+
"t": "Runbook of what Claude does behind the scenes, in the guide",
|
|
43
|
+
"s": "Runbook of what Claude does behind the scenes, in the guide",
|
|
44
|
+
"k": "x",
|
|
45
|
+
"h": "See what Claude does behind the scenes",
|
|
46
|
+
"d": "Claude's background jobs were explained one page at a time, and nothing listed them all together. One guide page lists every job Claude runs by itself: when it runs, what it reads, where you see the result, how to stop it, and what Claude fixes without asking."
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"n": 1208,
|
|
50
|
+
"t": "Reports that need you come first, one ✓ each (redesign R)",
|
|
51
|
+
"s": "Reports that need you come first, one ✓ each (redesign R)",
|
|
52
|
+
"k": "new"
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"v": "0.515.0",
|
|
58
|
+
"at": "2026-11-03T23:11:51.264Z",
|
|
59
|
+
"f": 4,
|
|
60
|
+
"prs": [
|
|
61
|
+
{
|
|
62
|
+
"n": 1206,
|
|
63
|
+
"t": "Intelligence reads the OKR file as its goal list (redesign A)",
|
|
64
|
+
"s": "Intelligence reads the OKR file as its goal list (redesign A)",
|
|
65
|
+
"k": "new"
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
},
|
|
35
69
|
{
|
|
36
70
|
"v": "0.514.0",
|
|
37
71
|
"at": "2026-11-03T16:19:12.685Z",
|
|
@@ -402,100 +436,6 @@
|
|
|
402
436
|
"d": "After a chat changed something, the Viewer still showed the old version until you refreshed. Now it updates on its own."
|
|
403
437
|
}
|
|
404
438
|
]
|
|
405
|
-
},
|
|
406
|
-
{
|
|
407
|
-
"v": "0.496.0",
|
|
408
|
-
"at": "2026-11-03T00:29:32.573Z",
|
|
409
|
-
"f": 4,
|
|
410
|
-
"prs": [
|
|
411
|
-
{
|
|
412
|
-
"n": 1172,
|
|
413
|
-
"t": "Initiative chats find the initiative's workflow through pmos, never a lane",
|
|
414
|
-
"s": "Initiative chats find the initiative's workflow through pmos, never a lane",
|
|
415
|
-
"k": "fix",
|
|
416
|
-
"h": "Initiative chats find the right workflow",
|
|
417
|
-
"d": "An initiative's chat could miss the workflow it should follow. Now it always finds it, including the ones in your own library."
|
|
418
|
-
},
|
|
419
|
-
{
|
|
420
|
-
"n": 1170,
|
|
421
|
-
"t": "Keep a rejected ✦ suggestion rejected while its facts hold",
|
|
422
|
-
"s": "Keep a rejected ✦ suggestion rejected while its facts hold",
|
|
423
|
-
"k": "fix",
|
|
424
|
-
"h": "A no stays a no",
|
|
425
|
-
"d": "A ✦ suggestion you turned down could come back the next day. Now it stays rejected while nothing about it changes."
|
|
426
|
-
},
|
|
427
|
-
{
|
|
428
|
-
"n": 1171,
|
|
429
|
-
"t": "Tasks keep their mapped stage after a workflow switch on Home and Board",
|
|
430
|
-
"s": "Tasks keep their mapped stage after a workflow switch on Home and Board",
|
|
431
|
-
"k": "fix",
|
|
432
|
-
"h": "Tasks keep their stage",
|
|
433
|
-
"d": "Switching an initiative's workflow could move its tasks to the wrong stage. Now they keep the stage they were mapped to."
|
|
434
|
-
},
|
|
435
|
-
{
|
|
436
|
-
"n": 1173,
|
|
437
|
-
"t": "Calmer Board top bar",
|
|
438
|
-
"s": "Calmer Board top bar",
|
|
439
|
-
"k": "new",
|
|
440
|
-
"h": "A calmer Board top bar",
|
|
441
|
-
"d": "The Board's top bar was crowded. Now it is lighter, with fewer controls in your way."
|
|
442
|
-
},
|
|
443
|
-
{
|
|
444
|
-
"n": 1165,
|
|
445
|
-
"t": "Pages show something at once while they read",
|
|
446
|
-
"s": "Pages show something at once while they read",
|
|
447
|
-
"k": "new",
|
|
448
|
-
"h": "Pages show something at once",
|
|
449
|
-
"d": "A page stayed blank while it read its data. Now it shows its shape right away and fills in as data arrives."
|
|
450
|
-
}
|
|
451
|
-
]
|
|
452
|
-
},
|
|
453
|
-
{
|
|
454
|
-
"v": "0.495.0",
|
|
455
|
-
"at": "2026-11-02T23:31:50.485Z",
|
|
456
|
-
"f": 4,
|
|
457
|
-
"prs": [
|
|
458
|
-
{
|
|
459
|
-
"n": 1164,
|
|
460
|
-
"t": "Report fixes from the live check of 0.494.0",
|
|
461
|
-
"s": "Report fixes from the live check of 0.494.0",
|
|
462
|
-
"k": "fix",
|
|
463
|
-
"h": "Report fixes from a live check",
|
|
464
|
-
"d": "A live check of reports found several small problems. They are fixed now."
|
|
465
|
-
},
|
|
466
|
-
{
|
|
467
|
-
"n": 1167,
|
|
468
|
-
"t": "Keep a live New workflow draft on the Workflows list",
|
|
469
|
-
"s": "Keep a live New workflow draft on the Workflows list",
|
|
470
|
-
"k": "fix",
|
|
471
|
-
"h": "New workflow drafts stay put",
|
|
472
|
-
"d": "A workflow draft you were making could vanish from the list. Now it stays there until you finish or remove it."
|
|
473
|
-
},
|
|
474
|
-
{
|
|
475
|
-
"n": 1166,
|
|
476
|
-
"t": "Initiative telemetry card starts collapsed",
|
|
477
|
-
"s": "Initiative telemetry card starts collapsed",
|
|
478
|
-
"k": "fix",
|
|
479
|
-
"h": "A quieter initiative page",
|
|
480
|
-
"d": "The progress card opened fully on every initiative. Now it starts folded, and you open it when you want it."
|
|
481
|
-
},
|
|
482
|
-
{
|
|
483
|
-
"n": 1168,
|
|
484
|
-
"t": "A sent chat message shows at once",
|
|
485
|
-
"s": "A sent chat message shows at once",
|
|
486
|
-
"k": "fix",
|
|
487
|
-
"h": "Sent messages show at once",
|
|
488
|
-
"d": "A chat message you sent took a moment to appear. Now it shows the moment you send it."
|
|
489
|
-
},
|
|
490
|
-
{
|
|
491
|
-
"n": 1169,
|
|
492
|
-
"t": "A default workflow, set with a star in Settings › Workflows",
|
|
493
|
-
"s": "A default workflow, set with a star in Settings › Workflows",
|
|
494
|
-
"k": "new",
|
|
495
|
-
"h": "Pick a default workflow",
|
|
496
|
-
"d": "Every new initiative needed its workflow chosen by hand. Now you star one in Settings and new work uses it."
|
|
497
|
-
}
|
|
498
|
-
]
|
|
499
439
|
}
|
|
500
440
|
]
|
|
501
441
|
}
|
package/payload/KIT-VERSION
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
kernel-source: pmos@
|
|
2
|
-
assembled: 2026-11-
|
|
1
|
+
kernel-source: pmos@454e5b13
|
|
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-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-11-04T00:00:04Z"}
|
|
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-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-11-04T00:00:04Z"}
|
|
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-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-11-04T00:00:04Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
package/payload/state/log.md
CHANGED
|
@@ -36,19 +36,22 @@ const json = (r, b, status = 200) => r.fulfill({ status, contentType: 'applicati
|
|
|
36
36
|
srv = spawn(process.execPath, [path.join(__dirname, 'server.js')], { cwd: __dirname, stdio: 'ignore', env: T.env({ PMOS_WS_PORT: String(T.PORT) }) });
|
|
37
37
|
await T.waitForServer(BASE);
|
|
38
38
|
browser = await chromium.launch({ executablePath: exe });
|
|
39
|
-
|
|
39
|
+
/* One clock for the page and the stand-ins: today at 11:00, so "Rail review" (30 minutes on) and the meeting 40 minutes back are today
|
|
40
|
+
* at any hour the suite runs. Unpinned, after 23:30 the review fell on tomorrow and C2 failed. */
|
|
41
|
+
const NOW = new Date(); NOW.setHours(11, 0, 0, 0);
|
|
42
|
+
const W = F.world(NOW);
|
|
40
43
|
const tap = (p, sel) => p.$eval('#czViewer ' + sel, (b) => b.click());
|
|
41
44
|
const text = (p) => p.evaluate(() => (document.querySelector('#czViewer') || {}).innerText || '');
|
|
42
45
|
const weekReads = (S) => S.gets.filter((g) => g.startsWith('/api/calendar-views')).length;
|
|
43
46
|
const ALL = [];
|
|
44
|
-
const open = async (url, o) => { const X = await F.open(browser, BASE, url, { noLive: true, ...o }); ALL.push(X); return X; };
|
|
47
|
+
const open = async (url, o) => { const X = await F.open(browser, BASE, url, { noLive: true, now: NOW, ...o }); ALL.push(X); return X; };
|
|
45
48
|
|
|
46
49
|
/* C1 the strip (on a Saturday or Sunday no day of the Mon–Fri strip is today, so none is pressed) */
|
|
47
50
|
const A = await open('/premium-viewer.html?open=calendar');
|
|
48
51
|
const p = A.page;
|
|
49
52
|
// Viewer kit (XvACal): the calendar opens on the day, with today's summary first; the week and month are in ⋯
|
|
50
53
|
ok('C1 the calendar opens in the Viewer card on the day', await until(() => p.evaluate(() => document.querySelectorAll('#czViewer .vzc-dy').length === 5 && document.querySelectorAll('#czViewer .vzc-de').length > 0), 15000));
|
|
51
|
-
const weekday = ![0, 6].includes(
|
|
54
|
+
const weekday = ![0, 6].includes(NOW.getDay());
|
|
52
55
|
await until(() => p.evaluate(() => [...document.querySelectorAll('#czViewer .vzc-dy')].every((d) => d.getBoundingClientRect().height >= 44)), 8000); // the Meetings stylesheet loads after the day is drawn
|
|
53
56
|
const strip = await p.evaluate(() => [...document.querySelectorAll('#czViewer .vzc-dy')].map((d) => ({ dow: d.querySelector('span').textContent, on: d.getAttribute('aria-pressed'), h: Math.round(d.getBoundingClientRect().height) })));
|
|
54
57
|
ok('C1 the strip has Monday to Friday and today is pressed on a weekday', strip.map((d) => d.dow).join() === 'MON,TUE,WED,THU,FRI' && strip.filter((d) => d.on === 'true').length === (weekday ? 1 : 0), JSON.stringify(strip));
|
|
@@ -47,7 +47,7 @@ const finish = async (code) => {
|
|
|
47
47
|
process.on('unhandledRejection', (e) => { console.error(e); finish(1); });
|
|
48
48
|
|
|
49
49
|
const BUDGETS = [{ id: 'docs', covers: 'documentation upkeep', cap: 0.2 }, { id: 'ops', covers: 'pipelines and deploys', cap: 0.1 }]; // as parseBudgets reads planning/okrs/health-budgets.md
|
|
50
|
-
const mk = (o) => IP.build(F.facts(Object.assign({ pinned: 'all', budgets: BUDGETS }, o))); // All goals pinned, so Goals opens on every goal
|
|
50
|
+
const mk = (o) => IP.build(F.facts(Object.assign({ pinned: 'all', budgets: BUDGETS, self: true }, o))); // the PMOS workspace itself: its own agent tiles show (redesign A) // All goals pinned, so Goals opens on every goal
|
|
51
51
|
/* the Board's measures, as GET /api/board-metrics-premium answers them: two with a series, one to look at, one the engine has not derived */
|
|
52
52
|
const BMR = { ok: true, state: 'ready', model: { tiles: [
|
|
53
53
|
{ key: 'corrections', value: '2.6', ser: [3.1, 2.9, 2.6] }, { key: 'merges', value: '174', ser: [150, 160, 174] },
|
|
@@ -351,7 +351,7 @@ const BMR = { ok: true, state: 'ready', model: { tiles: [
|
|
|
351
351
|
{
|
|
352
352
|
const R = await open(), q = R.page;
|
|
353
353
|
const o = await q.evaluate(() => ({ cols: [...document.querySelectorAll('#outTbl .ins-th [role="columnheader"]')].map((c) => c.textContent).join('|'), rows: [...document.querySelectorAll('#outTbl [data-ini]')].map((r) => r.dataset.ini), sso: document.querySelector('#outTbl [data-ini="sso-partners"]').textContent.replace(/\s+/g, ' '), decide: document.querySelectorAll('#outTbl [data-verdict]').length, keep: document.querySelectorAll('#outTbl [data-keep]').length, shown: !document.querySelector('#pOutcomes').hidden }));
|
|
354
|
-
ok('O1 Did it work? sits under the key results with the columns Promised, Got and Verdict, and counts what is to judge and in window', o.shown && o.cols === 'Did it work? 1 to judge · 2 in window|Promised|Got|Verdict', o.cols);
|
|
354
|
+
ok('O1 Did it work? sits under the key results with the columns Promised, Check, Got and Verdict (redesign A: the check date is a column), and counts what is to judge and in window', o.shown && o.cols === 'Did it work? 1 to judge · 2 in window|Promised|Check|Got|Verdict', o.cols);
|
|
355
355
|
ok('O1 it lists only the work to judge and in its window; a row says what its goal promised and what it got, and its date', o.rows.join() === 'refund-reason-codes,sso-partners,audit-log-export' && /Partner activation \+30 pts/.test(o.sso) && /\+8 pts/.test(o.sso) && /Judged Dec 31/.test(o.sso) && /shipped Oct 23/.test(o.sso), JSON.stringify(o));
|
|
356
356
|
ok('O1 a promise with no reading has no drafted verdict: Decide only, no ✓', o.decide === 1 && o.keep === 0, JSON.stringify(o));
|
|
357
357
|
await R.ctx.close();
|
|
@@ -359,7 +359,7 @@ const BMR = { ok: true, state: 'ready', model: { tiles: [
|
|
|
359
359
|
ok('O1 a guardrail is read from the readings: since Audit log export shipped, partner activation held or got worse', /Partner activation: worse by 2 pts/.test(await txt('#outTbl [data-ini="audit-log-export"]', R2.page)), await txt('#outTbl [data-ini="audit-log-export"]', R2.page));
|
|
360
360
|
await R2.ctx.close();
|
|
361
361
|
/* churn met its promise (4% down to 1.8%): Claude drafts Keep, one ✓ records it with the drafted line, and Undo takes it back */
|
|
362
|
-
const met = F.facts({ pinned: 'all', budgets: BUDGETS }); met.outcomes.find((x) => x.id === 'krm-churn').readings = [{ at: '2026-09-01', v: 4 }, { at: '2026-10-15', v: 1.8 }];
|
|
362
|
+
const met = F.facts({ pinned: 'all', budgets: BUDGETS, self: true }); met.outcomes.find((x) => x.id === 'krm-churn').readings = [{ at: '2026-09-01', v: 4 }, { at: '2026-10-15', v: 1.8 }];
|
|
363
363
|
const R3 = await open({ model: IP.build(met) }), q3 = R3.page, sent = [];
|
|
364
364
|
await q3.route(/\/api\/insights-records\/verdict(-undo)?$/, (r) => { sent.push([new URL(r.request().url()).pathname, JSON.parse(r.request().postData() || '{}')]); r.fulfill({ contentType: 'application/json', body: '{"ok":true}' }); });
|
|
365
365
|
const dv = await q3.evaluate(() => ({ text: document.querySelector('#outTbl [data-ini="refund-reason-codes"]').textContent.replace(/\s+/g, ' '), keep: (document.querySelector('#outTbl [data-keep]') || {}).getAttribute && document.querySelector('#outTbl [data-keep]').getAttribute('aria-label') }));
|
|
@@ -381,7 +381,7 @@ const BMR = { ok: true, state: 'ready', model: { tiles: [
|
|
|
381
381
|
const dc0 = await R4.page.evaluate(() => ({ rows: [...document.querySelectorAll('#outTbl [data-ini]')].map((r) => r.dataset.ini).join(), foot: (document.querySelector('#decFold') || {}).textContent }));
|
|
382
382
|
await R4.page.click('#decFold'); await sleep(80);
|
|
383
383
|
const dc = await R4.page.evaluate(() => (document.querySelector('#outTbl [data-ini="refund-reason-codes"]') || {}).textContent || '');
|
|
384
|
-
ok('O2 a decided row folds under one line that counts the verdicts; opened, it says Kept and what was learned', !/refund-reason-codes/.test(dc0.rows) &&
|
|
384
|
+
ok('O2 a decided row folds under one line that counts the verdicts, zero counts left out (redesign A); opened, it says Kept and what was learned', !/refund-reason-codes/.test(dc0.rows) && (dc0.foot || '').trim() === 'Decided 1 · 1 kept' && /Kept/.test(dc) && /Codes work\./.test(dc), JSON.stringify([dc0, dc]));
|
|
385
385
|
await R4.ctx.close();
|
|
386
386
|
}
|
|
387
387
|
|
|
@@ -390,9 +390,9 @@ const BMR = { ok: true, state: 'ready', model: { tiles: [
|
|
|
390
390
|
const R = await open(), q = R.page;
|
|
391
391
|
await q.click('#moreB'); await q.click('#moreMenu [data-more=\"library\"]');
|
|
392
392
|
/* re-pointed (premium-uncovered-features, IsLibrary): the bar hands New report to Home, Found in your work is cards, the reports are Report · Next · Status */
|
|
393
|
-
const lb = await q.evaluate(() => ({ side: !document.querySelector('#libSide').hidden, nr: document.querySelector('#newRep').getAttribute('href'), found: document.querySelector('#listRows .ins-fdh b').textContent, fcards: [...document.querySelectorAll('#listRows .ins-fcard')].map((c) => c.dataset.f + ':' + [...c.querySelectorAll('.ins-btn[data-fx]')].map((b) => b.textContent).join('/')).join(), heads: [...document.querySelectorAll('#repAsks .ins-rlh span')].map((s) => s.textContent).join('|'), rows: [...document.querySelectorAll('#listRows .ins-rl:not(.ins-rlh)')].map((r) => r.querySelector('.nm b').textContent + '|' + r.querySelector('.nx').textContent + '|' + r.querySelector('.st').textContent).join(' / '), setups: document.querySelectorAll('#listRows [data-setup]').length, asks: document.querySelectorAll('#listRows [data-ask]').
|
|
393
|
+
const lb = await q.evaluate(() => ({ side: !document.querySelector('#libSide').hidden, nr: document.querySelector('#newRep').getAttribute('href'), found: document.querySelector('#listRows .ins-fdh b').textContent, fcards: [...document.querySelectorAll('#listRows .ins-fcard')].map((c) => c.dataset.f + ':' + [...c.querySelectorAll('.ins-btn[data-fx]')].map((b) => b.textContent).join('/')).join(), heads: [...document.querySelectorAll('#repAsks .ins-rlh span')].map((s) => s.textContent).join('|'), rows: [...document.querySelectorAll('#repNeeds .ins-rl:not(.ins-rlh), #listRows .ins-rl:not(.ins-rlh)')].map((r) => r.querySelector('.nm b').textContent + '|' + r.querySelector('.nx').textContent + '|' + r.querySelector('.st').textContent).join(' / '), needs: [...document.querySelectorAll('#repNeeds .ins-nrow .nm b')].map((b) => b.textContent).join('|'), setups: document.querySelectorAll('#repNeeds [data-setup], #listRows [data-setup]').length, asks: new Set([...document.querySelectorAll('#repNeeds [data-ask], #listRows [data-ask]')].map((e) => e.dataset.ask)).size, cards: [...document.querySelectorAll('#listRows .ins-dcard')].map((c) => c.dataset.d + (c.querySelector('.ins-pinned') ? '*' : '')).join(), cap: document.querySelector('#repCap').textContent, old: !!document.querySelector('#listSeg, #sugB') }));
|
|
394
394
|
ok('L1 Reports and dashboards opens as a side panel from ⋯; New report in Home hands off there, and Found in your work holds the finding as a card', lb.side && /^\/premium\.html\?ws=w1&ask=insights&q=Make%20a%20new%20report/.test(lb.nr) && lb.found === 'Found in your work · 1' && lb.fcards === 'f-1:Set it up/Not a report' && lb.cap === '9' && !lb.old, JSON.stringify(lb));
|
|
395
|
-
ok('L1 the reports table is Report, Next and Status; a draft says "Draft ready for" its date; a request says
|
|
395
|
+
ok('L1 the reports table is Report, Next and Status (what needs you first, redesign A); a draft says "Draft ready for" its date; a request says Working; a ready proposal waits in Needs you (redesign R)', lb.heads === 'Report|Next|Status' && /Draft ready for Oct 14/.test(lb.rows) && /Partner update\|First draft [^|]+\|Runs every other monday/i.test(lb.rows) && /(^|\|)Churn for finance(\||$)/.test(lb.needs) && lb.setups === 1 && lb.asks === 2, JSON.stringify([lb.rows, lb.needs]));
|
|
396
396
|
ok('L1 dashboards are cards; only the pinned one carries the pin', lb.cards === 'all*,pd-1,p:Growth,p:Reliability', lb.cards);
|
|
397
397
|
const rs = []; await q.route('**/api/report-setups/*', (r) => { rs.push([new URL(r.request().url()).pathname.replace('/api/report-setups/', ''), JSON.parse(r.request().postData() || '{}')]); r.fulfill({ contentType: 'application/json', body: '{"ok":true,"drafted":{"slot":"2026-10-23","path":"planning/reports/x/2026-10-23.md"}}' }); });
|
|
398
398
|
const lastRs = () => rs[rs.length - 1] || [null, {}];
|
|
@@ -412,7 +412,7 @@ const BMR = { ok: true, state: 'ready', model: { tiles: [
|
|
|
412
412
|
await q.click('#czViewer [data-st="brief"]'); await sleep(80);
|
|
413
413
|
ok('L2 its Brief says what it is for and for whom', /What moved for partners this fortnight/.test(await txt('#czViewer .ins-gb', q)) && /Leadership/.test(await txt('#czViewer .ins-gb', q)));
|
|
414
414
|
await vwClose(q);
|
|
415
|
-
await q.click('#
|
|
415
|
+
await q.click('#repBody [data-ask="a-1"]'); const s3 = await until(() => vw(q));
|
|
416
416
|
await q.click('#czViewer [data-act="chg"]'); await q.click('#czViewer [data-act="change"]'); const need = await txt('#czViewer [data-msg]', q);
|
|
417
417
|
await q.fill('#czViewer [data-chg]', 'quarterly'); await q.click('#czViewer [data-act="change"]'); await until(() => rs.length > 2, 4000);
|
|
418
418
|
ok('L2 a request from Home opens with what you asked and its proposal; Change it needs words, then posts the correction', s3 && !s3.wait && /A request from Home/.test(s3.text) && /A monthly churn report for finance/.test(s3.text) && /Set it up/.test(s3.text) && /Say what to change/.test(need) && lastRs()[0] === 'change' && lastRs()[1].id === 'a-1' && lastRs()[1].text === 'quarterly', JSON.stringify([s3, need, rs]));
|
|
@@ -420,13 +420,13 @@ const BMR = { ok: true, state: 'ready', model: { tiles: [
|
|
|
420
420
|
ok('L2 every Library press went to a report-setups route', R.posts.every((x) => /^POST \/api\/report-setups\/(setup|pause|change)$/.test(x)), R.posts.join());
|
|
421
421
|
await q.click('#listRows [data-d="p:Reliability"]'); await sleep(150);
|
|
422
422
|
ok('L3 a dashboard card closes the panel and opens the key results on that view', (await txt('#viewName', q)) === 'Reliability' && (await q.evaluate(() => document.querySelector('#libSide').hidden)) && (await q.evaluate(() => [...document.querySelectorAll('#goalTbl [data-goal]')].map((r) => r.dataset.goal).join())) === 'krm-up,krm-churn');
|
|
423
|
-
await q.click('#moreB'); await q.click('#moreMenu [data-more=\"library\"]'); await q.locator('#
|
|
423
|
+
await q.click('#moreB'); await q.click('#moreMenu [data-more=\"library\"]'); await q.locator('#repBody [data-rpt]').first().click(); // a report file with no setup opens straight in the reader
|
|
424
424
|
ok('L4 a report opens in the page\'s reader, not the console (premium-report-read R1; re-pointed by premium-agreed-design: the reader is the report panel in the Viewer card)', await until(() => q.evaluate(() => !!document.querySelector('#czViewer:not([hidden]) .vz-rp0')), 5000) && /premium-intelligence\.html/.test(q.url()), q.url());
|
|
425
425
|
ok('L4 a draft report has Issue it in the reader, so nothing sends you to the console to issue it', await until(() => q.evaluate(() => !!document.querySelector('#czViewer .vz-rp0 .rp-sum [data-r="issue"]')), 8000) && await q.evaluate(() => !document.querySelector('#czViewer a[href*="console.html"]')));
|
|
426
426
|
await R.ctx.close();
|
|
427
427
|
const lead = { 'planning/reports/biweekly/2026-10-14.md': 'Activation slipped to 28% this fortnight.' };
|
|
428
428
|
const R2 = await open({ model: mk({ leads: lead }) });
|
|
429
|
-
await R2.page.click('#moreB'); await R2.page.click('#moreMenu [data-more=\"library\"]'); const ln = await R2.page.evaluate(() => ({ dash: [...document.querySelectorAll('#listRows [data-d] .ins-mini small')].map((e) => e.textContent), rep: [...document.querySelectorAll('#
|
|
429
|
+
await R2.page.click('#moreB'); await R2.page.click('#moreMenu [data-more=\"library\"]'); const ln = await R2.page.evaluate(() => ({ dash: [...document.querySelectorAll('#listRows [data-d] .ins-mini small')].map((e) => e.textContent), rep: [...document.querySelectorAll('#repBody [data-rpt] .nm small')].map((e) => e.textContent), su: document.querySelector('#listRows [data-setup] .nm small').textContent }));
|
|
430
430
|
ok('L5 each dashboard card draws its first goals, a report row quotes its file, a setup with no file says no draft is written yet', ln.dash[0] === 'Partner activation' && /“Activation slipped to 28% this fortnight\.”/.test(ln.rep[0]) && /No draft is written yet/.test(ln.su), JSON.stringify(ln));
|
|
431
431
|
await R2.ctx.close();
|
|
432
432
|
}
|
|
@@ -463,7 +463,7 @@ const BMR = { ok: true, state: 'ready', model: { tiles: [
|
|
|
463
463
|
{
|
|
464
464
|
const R = await open({ model: mk({ outcomes: [] }) });
|
|
465
465
|
/* re-pointed (batch 16 item 6): the ✦ line never says "no insight"; with no goal it says what Claude drafts, with ✦ to start it */
|
|
466
|
-
ok('I10 with no KR metric the ✦ line says no goal is set and offers to draft them, and the page draws no goal; Library still holds the report files', await R.page.evaluate(() => /has no goal yet/.test(document.querySelector('#brief').textContent) && !/no insight/i.test(document.querySelector('#brief').textContent) && !!document.querySelector('#insAct [data-okl="start"]') && /No key result is recorded for this workspace/.test(document.querySelector('#goalTbl').textContent) && document.querySelectorAll('#goalTbl [data-goal]').length === 0 && document.querySelectorAll('#
|
|
466
|
+
ok('I10 with no KR metric the ✦ line says no goal is set and offers to draft them, and the page draws no goal; Library still holds the report files', await R.page.evaluate(() => /has no goal yet/.test(document.querySelector('#brief').textContent) && !/no insight/i.test(document.querySelector('#brief').textContent) && !!document.querySelector('#insAct [data-okl="start"]') && /No key result is recorded for this workspace/.test(document.querySelector('#goalTbl').textContent) && document.querySelectorAll('#goalTbl [data-goal]').length === 0 && document.querySelectorAll('#repBody [data-rpt]').length === 2 && /draft the North Star/.test(document.querySelector("#insWhy").textContent) && !document.querySelector('#goalTbl .ins-th')));
|
|
467
467
|
await R.ctx.close();
|
|
468
468
|
const R2 = await open({ model: mk({ reportsOk: false, reports: [], missing: ['reports'] }) });
|
|
469
469
|
await R2.page.click('#moreB'); await R2.page.click('#moreMenu [data-more=\"library\"]');
|
|
@@ -500,7 +500,7 @@ const BMR = { ok: true, state: 'ready', model: { tiles: [
|
|
|
500
500
|
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } }), pg = await ctx.newPage(), errs = [];
|
|
501
501
|
pg.on('pageerror', (e) => errs.push(String(e)));
|
|
502
502
|
await pg.goto(BASE + '/premium-insights.html');
|
|
503
|
-
ok('E4 the real page says there is no goal, and has no script error', await until(() => pg.evaluate(() => /has no goal yet/.test(document.querySelector('#brief').textContent) && /No key result is recorded/.test(document.querySelector('#goalTbl').textContent)), 20000) && errs.length === 0, errs.join(' | '));
|
|
503
|
+
ok('E4 the real page lists the key results of this repository\'s OKR file with no reading, or says there is no goal, and has no script error (redesign A)', await until(() => pg.evaluate(() => (/^I read your OKR file/.test(document.querySelector('#brief').textContent) && !!document.querySelector('#goalTbl [data-kr]')) || (/has no goal yet/.test(document.querySelector('#brief').textContent) && /No key result is recorded/.test(document.querySelector('#goalTbl').textContent))), 20000) && errs.length === 0, errs.join(' | '));
|
|
504
504
|
await ctx.close();
|
|
505
505
|
}
|
|
506
506
|
{
|
|
@@ -570,7 +570,7 @@ const BMR = { ok: true, state: 'ready', model: { tiles: [
|
|
|
570
570
|
await q.route('**/api/insights-premium?ws=w2*', (r) => { asked.push(r.request().url()); r.fulfill({ contentType: 'application/json', body: JSON.stringify({ ok: true, state: 'ready', model: other }) }); });
|
|
571
571
|
await q.reload(); // the route above answers the second product from the first read on
|
|
572
572
|
const pf = await until(async () => { const x = await q.evaluate(() => ({ chip: (document.querySelector('#prodPick') || { getAttribute: () => '' }).getAttribute('aria-label'), all: (document.querySelector('#prodPick .ppk-all') || {}).textContent, groups: [...document.querySelectorAll('#goalTbl .ins-grp')].map((g) => g.querySelector('[role="cell"]').textContent), rows: document.querySelectorAll('#goalTbl [data-goal]').length, brief: document.querySelector('#brief').textContent, budgets: document.querySelectorAll('#goalTbl [data-budget]').length })); return x.groups.includes('Billing') ? x : null; }, 8000);
|
|
573
|
-
ok('M1 two products ticked (the chip says All): Portfolio reads the other product once and groups every goal by product; budgets stay with their product', !!pf && pf.chip === 'Products: all 2' && pf.all === 'All' && pf.groups.slice(0, 2).join() === 'pmos,Billing' && pf.rows === 6 && /^
|
|
573
|
+
ok('M1 two products ticked (the chip says All; the ✦ line sums All products, redesign A): Portfolio reads the other product once and groups every goal by product; budgets stay with their product', !!pf && pf.chip === 'Products: all 2' && pf.all === 'All' && pf.groups.slice(0, 2).join() === 'pmos,Billing' && pf.rows === 6 && /^Across your 2 products/.test(pf.brief) && pf.budgets === 0 && asked.length === 1, JSON.stringify(pf));
|
|
574
574
|
await q.click('#prodPick'); const pm = await q.evaluate(() => [...document.querySelectorAll('#ppMenu [data-pp]')].map((b) => b.querySelector('b').textContent + ':' + ((b.querySelector('em') || {}).textContent || '') + ':' + b.getAttribute('aria-checked')).join());
|
|
575
575
|
ok('M1 the list counts each product\'s goals and ticks both', pm === 'All products::true,pmos:5 goals:true,Billing:1 goal:true', pm);
|
|
576
576
|
await q.keyboard.press('Escape');
|
|
@@ -62,7 +62,7 @@ const ready = (model) => ({ status: 200, body: { ok: true, state: 'ready', model
|
|
|
62
62
|
const IP = require('./lib/insights-premium.js'), IF = require('./fixtures/insights-premium-facts.js');
|
|
63
63
|
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } }), q = await ctx.newPage(), errs = [], saves = [];
|
|
64
64
|
q.on('pageerror', (e) => errs.push(String((e && e.message) || e)));
|
|
65
|
-
await q.route('**/api/insights-premium*', (r) => r.fulfill({ contentType: 'application/json', body: JSON.stringify({ ok: true, state: 'ready', model: IP.build(IF.facts()), as_of: 1 }) }));
|
|
65
|
+
await q.route('**/api/insights-premium*', (r) => r.fulfill({ contentType: 'application/json', body: JSON.stringify({ ok: true, state: 'ready', model: IP.build(Object.assign(IF.facts(), { self: true })), as_of: 1 }) }));
|
|
66
66
|
await q.route('**/api/board-metrics-premium*', (r) => r.fulfill({ contentType: 'application/json', body: JSON.stringify(ready(metrics()).body) }));
|
|
67
67
|
await q.route('**/api/board-metric-targets/save', (r) => { saves.push(JSON.parse(r.request().postData() || '{}')); return r.fulfill({ contentType: 'application/json', body: JSON.stringify({ ok: true, targets: {} }) }); });
|
|
68
68
|
await q.route('**/api/briefing-premium/**', (r) => r.fulfill({ contentType: 'application/json', body: '{"ok":true,"turns":[],"working":false}' }));
|
package/workspace/changelog.json
CHANGED
|
@@ -1,4 +1,38 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"v": "0.516.0",
|
|
4
|
+
"at": "2026-11-04T00:00:07.601Z",
|
|
5
|
+
"f": 4,
|
|
6
|
+
"prs": [
|
|
7
|
+
{
|
|
8
|
+
"n": 1209,
|
|
9
|
+
"t": "Runbook of what Claude does behind the scenes, in the guide",
|
|
10
|
+
"s": "Runbook of what Claude does behind the scenes, in the guide",
|
|
11
|
+
"k": "x",
|
|
12
|
+
"h": "See what Claude does behind the scenes",
|
|
13
|
+
"d": "Claude's background jobs were explained one page at a time, and nothing listed them all together. One guide page lists every job Claude runs by itself: when it runs, what it reads, where you see the result, how to stop it, and what Claude fixes without asking."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"n": 1208,
|
|
17
|
+
"t": "Reports that need you come first, one ✓ each (redesign R)",
|
|
18
|
+
"s": "Reports that need you come first, one ✓ each (redesign R)",
|
|
19
|
+
"k": "new"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"v": "0.515.0",
|
|
25
|
+
"at": "2026-11-03T23:11:51.264Z",
|
|
26
|
+
"f": 4,
|
|
27
|
+
"prs": [
|
|
28
|
+
{
|
|
29
|
+
"n": 1206,
|
|
30
|
+
"t": "Intelligence reads the OKR file as its goal list (redesign A)",
|
|
31
|
+
"s": "Intelligence reads the OKR file as its goal list (redesign A)",
|
|
32
|
+
"k": "new"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
},
|
|
2
36
|
{
|
|
3
37
|
"v": "0.514.0",
|
|
4
38
|
"at": "2026-11-03T16:19:12.685Z",
|
|
@@ -62,6 +62,7 @@ const REPORT = '---\ntype: Report\ntitle: "Password login and sign-up"\n---\n\n#
|
|
|
62
62
|
|
|
63
63
|
function install(page, o = {}) {
|
|
64
64
|
const W = world(o.now || new Date());
|
|
65
|
+
const shift = o.now ? o.now.getTime() - Date.now() : 0; // a pinned clock (o.now): the stand-ins answer in the page's time
|
|
65
66
|
const S = { posts: [], gets: [], scene: o.scene || 'notes', W };
|
|
66
67
|
const json = (r, b, status = 200) => r.fulfill({ status, contentType: 'application/json', body: JSON.stringify(b) });
|
|
67
68
|
const body = (r) => { try { return JSON.parse(r.request().postData() || '{}'); } catch { return {}; } };
|
|
@@ -118,8 +119,8 @@ function install(page, o = {}) {
|
|
|
118
119
|
const u = new URL(r.request().url()), ev = u.searchParams.get('event'), note = u.searchParams.get('note');
|
|
119
120
|
const live = ev === 'ev-live' || note === 'n2';
|
|
120
121
|
/* a note with no calendar event has no event, brief or topics */
|
|
121
|
-
if (!ev && note && note !== 'n1' && note !== 'n2') return json(r, { ok: true, now_ms: Date.now(), event: null, note: { id: note, state: (S.store[note] || {}).state || 'ended' }, brief: null, material: [], topics: [], runs: [], captured: {}, tail: [] });
|
|
122
|
-
const base = { ok: true, now_ms: Date.now(), event: live ? { id: 'ev-live', title: 'Design crit', at: W.LIVE_NOTE.meeting.start, end: W.LIVE_NOTE.meeting.end, attendees: ['Ana', 'Wawan'] } : ev === 'ev-rail' ? { id: 'ev-rail', title: 'Rail review', at: iso(W.RAIL), attendees: ['Sam', 'Wawan'] } : { id: 'ev-sync', title: 'Viewer sync', at: W.NOTE.meeting.start, end: W.NOTE.meeting.end, attendees: ['Ana', 'Dita', 'Wawan'] },
|
|
122
|
+
if (!ev && note && note !== 'n1' && note !== 'n2') return json(r, { ok: true, now_ms: Date.now() + shift, event: null, note: { id: note, state: (S.store[note] || {}).state || 'ended' }, brief: null, material: [], topics: [], runs: [], captured: {}, tail: [] });
|
|
123
|
+
const base = { ok: true, now_ms: Date.now() + shift, event: live ? { id: 'ev-live', title: 'Design crit', at: W.LIVE_NOTE.meeting.start, end: W.LIVE_NOTE.meeting.end, attendees: ['Ana', 'Wawan'] } : ev === 'ev-rail' ? { id: 'ev-rail', title: 'Rail review', at: iso(W.RAIL), attendees: ['Sam', 'Wawan'] } : { id: 'ev-sync', title: 'Viewer sync', at: W.NOTE.meeting.start, end: W.NOTE.meeting.end, attendees: ['Ana', 'Dita', 'Wawan'] },
|
|
123
124
|
note: live ? { id: 'n2', state: 'recording' } : ev === 'ev-rail' ? null : { id: 'n1', state: 'ended' },
|
|
124
125
|
brief: { gist: ['Agree the rail before the Commit.'], purpose: 'Agree the rail before the Commit.', matters: [{ tag: 'Late', tone: 'bad', text: 'The SSO spike', meta: 'due 14 Oct' }, { tag: 'Changed', tone: '', text: 'The report panel design', meta: 'Oct 21' }], last: { id: 'n0', at: '2026-10-15T10:00:00Z', decisions: [{ text: 'a' }], actions: [{ text: 'b' }, { text: 'c' }] }, prep: 'Read the rail PRD.', initiative: { id: 'viewer', title: 'Viewer', ws: 'w1' } },
|
|
125
126
|
material: [{ kind: 'pmos-deck', title: 'Release deck', ref: 'decks/release.md', from: 'you' }, { kind: 'pmos-doc', title: 'Rail PRD', ref: 'planning/prd/rail.md', from: 'you' }, { kind: 'link', title: 'Figma: report panel', url: 'https://x.example/f', topic: 'The report panel' }],
|
|
@@ -189,6 +190,7 @@ async function open(browser, base, url, o = {}) {
|
|
|
189
190
|
const ctx = await browser.newContext({ viewport: { width: o.w || 1280, height: o.h || 900 }, acceptDownloads: true });
|
|
190
191
|
const page = await ctx.newPage(), errors = [];
|
|
191
192
|
page.on('pageerror', (e) => errors.push(String((e && e.message) || e)));
|
|
193
|
+
if (o.now) await page.clock.setSystemTime(o.now); // the page's Date starts at o.now and runs on; timers are real
|
|
192
194
|
const S = await install(page, o);
|
|
193
195
|
await page.addInitScript(RECORDER);
|
|
194
196
|
if (o.init) await page.addInitScript(o.init);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Guide
|
|
3
|
+
title: "What Claude does behind the scenes"
|
|
4
|
+
description: "The runbook of the workspace agent: each job Claude runs by itself, when it runs, what it reads, where its result shows, how to stop it, what it fixes by itself, the rules every job follows, and how to see what ran."
|
|
5
|
+
timestamp: 2026-11-03
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# What Claude does behind the scenes
|
|
9
|
+
|
|
10
|
+
Claude works in the background while the workspace runs. It reads your records and the tools you connected, it drafts, and it checks. It does not decide for you. A result waits for your ✓, most often under the blue ✦ of an initiative or as one ✦ in Home's chat. A background job never sends, posts, pushes or merges anything.
|
|
11
|
+
|
|
12
|
+
This page lists every job, so you know what runs and why. Each row links to the page with the full detail.
|
|
13
|
+
|
|
14
|
+
## The rules every job follows
|
|
15
|
+
|
|
16
|
+
- **Propose-only.** A background job files proposals. Nothing lands on an initiative, a task or a metric until you press ✓. ✕ drops a proposal, and Undo brings back what was there.
|
|
17
|
+
- **Every claim has a source.** An AI job files through one shared guard. The guard checks that each quote is really at the file and line it cites. A claim with no source that checks out is dropped. A proposal with a key that would write, apply, send or run a command is dropped.
|
|
18
|
+
- **Few at a time.** A run shows at most 3 proposals. In a quiet week it shows nothing.
|
|
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
|
+
- **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
|
+
- **Your hours.** Scheduled jobs run inside your working hours and never inside your quiet hours.
|
|
22
|
+
- **A spending limit.** Claude's checks spend at most the monthly credit you set in Settings › Claude's checks ($5, $10, $20 or $50). At the limit Claude pauses. It never spends beyond the limit.
|
|
23
|
+
- **Every run is logged.** Each job writes its own ledger in the runtime folder on this computer. Only you see it, and it never goes into a repository.
|
|
24
|
+
|
|
25
|
+
## The jobs
|
|
26
|
+
|
|
27
|
+
| Job | When it runs | What it reads | Where you see it | How to stop it |
|
|
28
|
+
|---|---|---|---|---|
|
|
29
|
+
| [Initiative Driver](initiative-driver.md) | An initiative is created, moved or sent back. The unblock sweep runs every 30 minutes in working hours. At most 3 runs at once. | The initiative record, its tasks and its Jira tickets | The Agent strip of the initiative, and Needs you | Board settings › Agent: Off. `PMOS_DRIVER=off` |
|
|
30
|
+
| [Found elsewhere](claude-proposes.md) | You add an initiative | Drive, Docs, Sheets, Slides, Jira, the calendar, meeting notes, Gmail, Slack and Confluence | The initiative's ✦ | `PMOS_PROPOSE=off` |
|
|
31
|
+
| [New dates, done and tidy names](claude-proposes.md) | Each morning, and when a date passes | The tasks of the initiative, and the evidence that work happened | The initiative's ✦ | `PMOS_PROPOSE=off` |
|
|
32
|
+
| [Grill](claude-proposes.md) | Before an initiative leaves a stage, when a forecast turns at risk, when scope or dates move a lot, and once a month on running work with late work. Also when you write "grill this" in the initiative's chat. | The initiative, your stored research, your meeting notes and the decisions that stand | "Claude grilled it" under the initiative's ✦ | Settings › Claude's checks: Off. `PMOS_GRILL=off` |
|
|
33
|
+
| [You decided this before](ask-the-past.md) | You add an initiative that repeats an earlier decision | `DECISIONS.md`, the decision records, the PM journal, `planning/out-of-scope/` and the initiative records | The initiative's ✦ | `pmos ai off past-repeat` |
|
|
34
|
+
| [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` |
|
|
35
|
+
| [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` |
|
|
36
|
+
| [Horizon scan](horizon-scan.md) | Daily by default, at most once a day. 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` |
|
|
37
|
+
| [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` |
|
|
38
|
+
| [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` |
|
|
39
|
+
| [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` |
|
|
40
|
+
| [Day tasks](proactive-engine.md) | You press Prepare my day. At most 5 sessions at once. | The sources of the brief, read-only | Today: a draft waits for Approve or Revise | Do not press Prepare my day |
|
|
41
|
+
| [Next prompt and Sharper](next-prompt.md) | After Claude replies, and when you stop typing a rough prompt | The reply and your message | Cards above the chat box. Nothing is sent. | Settings › Claude. `pmos ai off next-prompt`, `pmos ai off sharpen-prompt` |
|
|
42
|
+
| [Rehearse a plan](rehearse.md) | You ask Claude to move a date or cut a task or an initiative | Task links, initiative records, goals and program charters | Your session, before Claude makes the change | `pmos ai off rehearse` |
|
|
43
|
+
| [Data refresh](data-freshness.md) | In working hours: the calendar every 30 minutes, and Jira, My work and your tools every hour. Tracker items on Friday 15:30. | Each tool with your own connection, read-only | Each page shows how old its data is | Settings › Data refresh |
|
|
44
|
+
|
|
45
|
+
Ask about the past runs only when you ask a question in an initiative's chat. It answers from the records, with the file and line of each claim, and says **No record found** when no record answers.
|
|
46
|
+
|
|
47
|
+
## What Claude fixes by itself
|
|
48
|
+
|
|
49
|
+
Some problems are safe to fix with no question. Claude fixes these by itself and tells you only when it cannot.
|
|
50
|
+
|
|
51
|
+
- **A product folder on the wrong branch.** A document repository whose folder is not on its default branch looks empty. The workspace puts it back: it saves the records, checks that the merge has no conflict, merges, and pushes with no force. It never deletes or rewrites a branch. It never touches a code repository. When it cannot do this safely, Today shows one "Needs repair" card in plain words, with **Ask Claude to sort it out**. It tries again after 30 minutes, or after 10 minutes when it only waited for the network. `PMOS_AUTO_REPAIR=0` stops it.
|
|
52
|
+
- **Folders of finished work.** About every 6 hours the workspace removes a linked worktree only when its branch is merged, it holds no unsaved file, nothing used it for 14 days, and no session uses it. The branch is kept. `PMOS_AUTO_CLEAN=0` stops it.
|
|
53
|
+
- **A report source that does not answer.** A scheduled report waits up to 4 hours for a source that failed its check. Today shows "Check <source> for <report>" with **Check again**. Then the report is drafted without that source, and it says so. It never guesses a number.
|
|
54
|
+
- **A failed run.** A metric draft that failed tries again the next day. A Driver run that failed or timed out writes its reason in the ledger.
|
|
55
|
+
- **A source that was not reached.** It is shown as not reached, never as read. Today says which sources were not read.
|
|
56
|
+
|
|
57
|
+
Sync by itself is on by default. `PMOS_AUTO_SYNC=0` stops the sync, the repair and the clean-up.
|
|
58
|
+
|
|
59
|
+
## What a background job never does
|
|
60
|
+
|
|
61
|
+
- It sends, posts, replies, pushes and merges nothing. A message to a person is a draft that you send. A Jira change is a drafted comment in your outbox.
|
|
62
|
+
- It does not create an initiative. The Horizon scan proposes one, and you approve it.
|
|
63
|
+
- It does not record a verdict on an outcome. The verdict is always yours.
|
|
64
|
+
|
|
65
|
+
When you ask an agent session to do a piece of work, it can finish it, including acts that were yours alone. Each line it writes names the agent, so you see who did it. See [PM-only acts](pm-only-acts.md).
|
|
66
|
+
|
|
67
|
+
## See what ran, and turn it off
|
|
68
|
+
|
|
69
|
+
- `pmos ai status`: whether each AI job is on, whether its replay passed, and what its last run filed.
|
|
70
|
+
- `pmos ai log [<job>]`: the last audit lines. Each dropped proposal shows why.
|
|
71
|
+
- `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.
|
|
72
|
+
|
|
73
|
+
The AI jobs are `past-repeat`, `decision-memory`, `outcomes`, `drift`, `rehearse`, `next-prompt` and `sharpen-prompt`. A background run cannot change the switch.
|
package/workspace/guide/index.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
type: Guide
|
|
3
3
|
title: "Guide: start here"
|
|
4
|
-
timestamp: 2026-11-
|
|
4
|
+
timestamp: 2026-11-03
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Guide: start here
|
|
@@ -41,6 +41,7 @@ On a desktop the workspace is one frame. [Layout](layout.md) explains each part.
|
|
|
41
41
|
|
|
42
42
|
## Where to read next
|
|
43
43
|
|
|
44
|
+
- [What Claude does behind the scenes](agent-capabilities.md): every job Claude runs by itself, when it runs, what it reads, where you see it, how to stop it, and what it fixes by itself.
|
|
44
45
|
- [First run](welcome.md): test your Claude login, describe the project, choose its folder, search your tools read-only, and set up PMOS from Home.
|
|
45
46
|
- [The Briefing](briefing.md): the console's first page: Today's meetings, Needs you (Done, Not yet, Drop) and the Portfolio pulse.
|
|
46
47
|
- [Start the workspace without a terminal](launcher.md): the PMOS app, the login item and `pmos launcher`.
|
|
@@ -80,6 +81,8 @@ On a desktop the workspace is one frame. [Layout](layout.md) explains each part.
|
|
|
80
81
|
- [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.
|
|
81
82
|
- [Initiatives › Timeline](initiatives-timeline.md): when each initiative runs, one row per initiative, and how to reorder rows by their grip.
|
|
82
83
|
- [Workflows](workflows.md): Settings › Workflows, the stages each kind of initiative goes through, New workflow in the chat, and `pmos workflow`.
|
|
84
|
+
- [Intelligence reads your OKR file](intelligence-okr-goals.md): each key result of the OKR file is a row, with its reading or the source it needs; Did it work?, All products and Delivery trends.
|
|
85
|
+
- [Reports and dashboards: what needs you](reports-needs-you.md): the reports that wait for you come first, each with one ✓ (Issue, Confirm the definition, Move to a product, Resume); the first dashboard is drafted from the OKR file.
|
|
83
86
|
- [Expected outcomes](outcomes.md): every initiative says what should change once it ships and when to check it. Claude drafts each one from the PRD or the KR, you keep it with ✓, and `pmos outcomes`.
|
|
84
87
|
- [The next prompt, suggested and sharpened](next-prompt.md): after a reply, up to two prompts to send next; while you type a rough prompt, a sharper version in your style (Polish, Prompt engineer, Your own).
|
|
85
88
|
- [Rehearse a plan](rehearse.md): before you approve a date change or a cut, Claude shows what it breaks or helps in other initiatives, goals and program milestones, with the record each effect comes from, and `pmos rehearse`.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Guide
|
|
3
|
+
title: "Intelligence reads your OKR file"
|
|
4
|
+
description: "How Intelligence lists the key results of a product's OKR file, which ones have a reading, Did it work?, All products and Delivery trends."
|
|
5
|
+
timestamp: 2026-11-03
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Intelligence reads your OKR file
|
|
9
|
+
|
|
10
|
+
Intelligence (`/premium-intelligence.html`) uses the OKR file of a product as its goal list. It reads `pmos/planning/okrs/okrs-*.md` in the product repository, or `planning/okrs/` in PMOS itself. When a file says `Supersedes x.md`, Intelligence does not read `x.md`.
|
|
11
|
+
|
|
12
|
+
## The goal list
|
|
13
|
+
|
|
14
|
+
- **The period.** The first period in the period chip is the period of the OKR file, for example H2 2026. It is the default, so a filter for one quarter does not hide a goal of the half.
|
|
15
|
+
- **Each key result is a row.** The row shows the objective, the key result, its line from the file (baseline → target) and its due date.
|
|
16
|
+
- **A reading.** When a metric record names the key result (its `kr` field, for example `O1.KR2`), that record reads the key result. The row then shows the reading and its line.
|
|
17
|
+
- **Needs a source.** When nothing reads a key result, the row says which source the file names, for example New Relic, ServiceNow or a host. The row shows no number. Click the row to see the key result and **Connect** its source in Settings.
|
|
18
|
+
- **Other goals.** Metric records that read no key result show under **Other goals**.
|
|
19
|
+
- **+ New goal.** The form is filled from the first key result that nothing reads: its name, baseline, target, direction, due date and objective. Check it, then save. The saved goal reads that key result. It is not a second goal.
|
|
20
|
+
|
|
21
|
+
## The ✦ line
|
|
22
|
+
|
|
23
|
+
The ✦ line says what Intelligence read this week and the one call to make. When no key result has a reading yet, it says how many key results the file holds and which sources they wait on. It does not guess a number. After the findings from the readings, the strip also says when an outcome check is overdue and which initiatives are late.
|
|
24
|
+
|
|
25
|
+
## Did it work?
|
|
26
|
+
|
|
27
|
+
Each shipped initiative shows what it promised (its `outcome_metric`), the date to check it (its `outcome_horizon`), what it got and the verdict. A verdict shows only when one is recorded. A Done status alone does not make a verdict. **Decided** counts the recorded verdicts and leaves out a count of zero. The table also says how many initiatives have no promise. [Expected outcomes](outcomes.md) explains the promise.
|
|
28
|
+
|
|
29
|
+
## All products
|
|
30
|
+
|
|
31
|
+
When you tick two or more products, a table shows one row for each product: its North Star, how many key results have a reading, the late initiatives, the outcome checks that are overdue and the next step Claude would take. Click a row to open the Intelligence of that product. On a phone each row is a card.
|
|
32
|
+
|
|
33
|
+
## Delivery trends
|
|
34
|
+
|
|
35
|
+
On a product repository, Delivery trends does not show **Merged a week** or **Corrections / 100 runs**. Those tiles measure PMOS agents, so they show only on the PMOS workspace. When the history is shorter than 30 days, **Idle 30+ days** shows — and says the date it is known from.
|
|
36
|
+
|
|
37
|
+
## Reports and dashboards
|
|
38
|
+
|
|
39
|
+
The panel shows **Needs you** first: the drafts that wait for you and the asks that are ready. The ready-made reports are next, folded under **Drafting on their moment**. Your own reports and dashboards follow.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Guide
|
|
3
|
+
title: "Reports and dashboards: what needs you"
|
|
4
|
+
description: "How the Reports and dashboards panel in Intelligence lists the reports that wait for you first, each with one ✓, drafts the first dashboard from the OKR file, and how + makes a report."
|
|
5
|
+
timestamp: 2026-11-03
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Reports and dashboards: what needs you
|
|
9
|
+
|
|
10
|
+
Open Intelligence (`/premium-intelligence.html`), then **⋯ › Reports and dashboards**. The panel shows these parts, in this order:
|
|
11
|
+
|
|
12
|
+
1. **Needs you.** Each report that waits for you. A row shows the report name, the draft date, the readers, and the first line of the headline. Press the name to open the report or its setup. Each row has one ✓:
|
|
13
|
+
- **Issue**: the draft is ready. The ✓ issues the draft as you see it. Send it from your own mail.
|
|
14
|
+
- **Confirm the definition**: an agent proposed the definition of this report (for example the daily report), and nobody confirmed it yet. The ✓ changes `status: draft` to `status: stable` in the definition. It changes nothing else.
|
|
15
|
+
- **Move to <product>**: the report is about a different product than the one it is in. For example, the Scarlet assessment report was set up in the IDPF workspace. The ✓ moves the definition, the drafts and the setup to that product. A report that has an issued issue is not moved, because an issued report stays where it was issued.
|
|
16
|
+
- **Resume**: the report is paused. The ✓ starts its schedule again.
|
|
17
|
+
2. **Drafting on their moment.** The ready-made reviews (quarter, milestone, stage, did it work). They draft when their moment comes. Open the fold to see each switch.
|
|
18
|
+
3. **Weekly updates.** One line tells how many are on and off. Open the fold to see each switch.
|
|
19
|
+
4. **Yours.** The other reports that you set up.
|
|
20
|
+
5. **Dashboards.** When no dashboard is saved, Claude drafts the first one from the OKR file: one tile for each key result. A tile that has a reading shows the value, its reader and the date. A tile with no reading shows the source it needs, and its ✓ opens Settings › Connectors. Press ✓ on the card to keep the dashboard. It keeps the key results that have a reading.
|
|
21
|
+
|
|
22
|
+
A report from the daily, weekly or monthly definition has the title of its definition, for example "Daily report", not its id. Its preview is the headline. The line that says the definition is a draft is not the preview.
|
|
23
|
+
|
|
24
|
+
## + in the panel
|
|
25
|
+
|
|
26
|
+
**+** opens the report that Claude drafts from the view on the screen. It does not open an empty chat. With all products on the screen, **+** opens Home, because a report is for one product.
|
|
27
|
+
|
|
28
|
+
## Edit chart
|
|
29
|
+
|
|
30
|
+
**Edit chart** on a key result that has no reading shows the connector card for its source. When no dashboard is saved, **Save chart** keeps the drafted dashboard first.
|
|
31
|
+
|
|
32
|
+
## The product page
|
|
33
|
+
|
|
34
|
+
The ✦ line on a product page is the same line as on Intelligence for that product. "Your weekly update is ready to send" shows only in a week with news. In a quiet week the Weekly update row says "Nothing new this week · skipped".
|