create-pmos 0.59.2 → 0.60.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 +1 -1
- package/payload/KIT-VERSION +1 -1
- package/payload/planning/evals/eval-calibration.md +1 -1
- package/payload/planning/okrs/health-budgets.md +1 -1
- package/payload/scripts/delivery-index.js +85 -7
- package/payload/scripts/workflow-state.js +3 -3
- package/payload/state/discoveries.md +1 -1
- package/workspace/REMOTE.md +1 -1
- package/workspace/fixtures/initiatives-api/sheet.json +29 -7
- package/workspace/guide/index.md +3 -3
- package/workspace/guide/initiatives-screen.md +25 -1
- package/workspace/guide/layout.md +7 -9
- package/workspace/guide/proactive-engine.md +1 -0
- package/workspace/guide/today.md +23 -1
- package/workspace/lib/calendar-memory.js +257 -0
- package/workspace/lib/console-state.js +1 -1
- package/workspace/lib/doc-category.js +3 -1
- package/workspace/lib/engine/delivery-index.js +85 -7
- package/workspace/lib/engine/workflow-state.js +3 -3
- package/workspace/lib/initiative-files.js +59 -1
- package/workspace/lib/initiative-rows.js +14 -2
- package/workspace/lib/mobile-nav.js +1 -1
- package/workspace/lib/plan-tree.js +44 -5
- package/workspace/lib/proactive-cli.js +16 -1
- package/workspace/lib/routes-initiatives.js +165 -9
- package/workspace/package-lock.json +1 -1
- package/workspace/package.json +1 -1
- package/workspace/public/index.html +1 -1
- package/workspace/public/pm/components.css +70 -0
- package/workspace/public/pm/dest-initiatives.js +7 -1
- package/workspace/public/pm/dest-settings.js +1 -0
- package/workspace/public/pm/frame-copy.js +14 -2
- package/workspace/public/pm/initiative-sheet.js +42 -10
- package/workspace/public/pm/initiatives-api.js +15 -0
- package/workspace/public/pm/initiatives-copy.js +13 -0
- package/workspace/public/pm/initiatives-ui.js +14 -0
- package/workspace/public/pm/mobile.js +4 -5
- package/workspace/public/pm/registry.js +1 -1
- package/workspace/public/pm/tab-term.js +6 -50
- package/workspace/public/pm/today-agenda.js +305 -0
- package/workspace/public/pm/today-copy.js +54 -2
- package/workspace/public/pm/today-day.js +23 -3
- package/workspace/public/pm/today-goals.js +7 -1
- package/workspace/server.js +71 -63
- package/workspace/skills/morning-brief.skill +6 -1
- package/workspace/test-files.json +2 -0
- package/workspace/test-lists/scan-consistency.json +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.60.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": {
|
package/payload/KIT-VERSION
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Eval calibration log"
|
|
4
4
|
description: "Append-only record of every PM override of an evaluator verdict."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-06T15:12:25Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Health budgets"
|
|
4
4
|
description: "Closed registry of health-budget anchors (D59), edited only by the PM."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-06T15:12:25Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* const D = require('./delivery-index');
|
|
7
7
|
* const idx = D.deliveryIndex(root, ids); // { ok, reason?, byBranch:Set, byMention:Set }
|
|
8
|
-
* D.attribution(idx, id) // 'branch' | 'mention' | null | 'unknown'
|
|
8
|
+
* D.attribution(idx, id) // 'branch' | 'mention' | 'change' | null | 'unknown'
|
|
9
9
|
* D.isDeliveryPath('workspace/server.js') // true — a build path, not planning prose
|
|
10
10
|
*
|
|
11
11
|
* WHY THIS EXISTS. This derivation shipped inside `workspace/server.js` as a module-private
|
|
@@ -101,13 +101,13 @@ const deliveryCache = new Map(); // root -> { head, index }
|
|
|
101
101
|
* any commit subject into a delivery claim
|
|
102
102
|
* @returns {{ok:boolean, reason?:string, byBranch:Set<string>, byMention:Set<string>}}
|
|
103
103
|
*/
|
|
104
|
-
function deliveryIndex(root, ids) {
|
|
104
|
+
function deliveryIndex(root, ids, opts) {
|
|
105
105
|
const t = trunkRef(root);
|
|
106
106
|
if (!t) {
|
|
107
107
|
return {
|
|
108
108
|
ok: false,
|
|
109
109
|
reason: 'no trunk branch (main/master) is reachable from this repository — delivery cannot be derived',
|
|
110
|
-
byBranch: new Set(), byMention: new Set(),
|
|
110
|
+
byBranch: new Set(), byMention: new Set(), byChange: new Set(),
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
113
|
const hit = deliveryCache.get(root);
|
|
@@ -119,7 +119,7 @@ function deliveryIndex(root, ids) {
|
|
|
119
119
|
return {
|
|
120
120
|
ok: false,
|
|
121
121
|
reason: `git could not read the history at ${t.ref}${r.err ? ` — ${r.err}` : ''}`,
|
|
122
|
-
byBranch: new Set(), byMention: new Set(),
|
|
122
|
+
byBranch: new Set(), byMention: new Set(), byChange: new Set(),
|
|
123
123
|
};
|
|
124
124
|
}
|
|
125
125
|
const byBranch = new Set();
|
|
@@ -137,11 +137,87 @@ function deliveryIndex(root, ids) {
|
|
|
137
137
|
// the safe direction and the one the surface already errs in.
|
|
138
138
|
for (const id of known) if (id.length > 3 && msg.includes(id)) byMention.add(id);
|
|
139
139
|
}
|
|
140
|
-
|
|
140
|
+
// 3. THE CHANGE THAT CARRIED THE SPEC (scan-consistency R1). See changeSignal below.
|
|
141
|
+
const byChange = changeSignal(root, t.head, known, opts);
|
|
142
|
+
const index = { ok: true, byBranch, byMention, byChange };
|
|
141
143
|
deliveryCache.set(root, { head: t.head, index });
|
|
142
144
|
return index;
|
|
143
145
|
}
|
|
144
146
|
|
|
147
|
+
/*
|
|
148
|
+
* THE THIRD SIGNAL — a change on the trunk that touched this initiative's OWN documents and build
|
|
149
|
+
* paths together (scan-consistency R1).
|
|
150
|
+
*
|
|
151
|
+
* Signals 1 and 2 both need the id spelled in a commit message. On this repository most pull
|
|
152
|
+
* requests ship on `claude/<slug>-<suffix>` branches and their commits name the area (`feat(workspace):
|
|
153
|
+
* …`), not the id, so 102 of 283 initiatives read `build pending` although their PRD, record and code
|
|
154
|
+
* merged in one pull request. The tie is concrete and git holds it: the pull request that changed
|
|
155
|
+
* `planning/initiatives/<id>.md` (or the PRD the record names) ALSO changed build files.
|
|
156
|
+
*
|
|
157
|
+
* A UNIT is one first-parent commit of the trunk: a merge commit (its diff against the first parent
|
|
158
|
+
* is the whole pull request), a squash merge, or a direct commit. Two guards keep it from becoming a
|
|
159
|
+
* similarity score:
|
|
160
|
+
* · the unit must change at least one build path (isDeliveryPath) — a docs-only pull request
|
|
161
|
+
* delivers nothing;
|
|
162
|
+
* · the unit may touch the documents of at most MAX_CO_INITIATIVES initiatives. A sweep that edits
|
|
163
|
+
* forty records while changing a script is evidence for none of them.
|
|
164
|
+
* A merged branch whose name, less its random suffix, IS a known id (`claude/jira-wbs-kpamaz`) counts
|
|
165
|
+
* too, when its pull request changed build paths.
|
|
166
|
+
*/
|
|
167
|
+
const MAX_CO_INITIATIVES = 2;
|
|
168
|
+
const RECORD_DIRS = ['planning/initiatives', 'pmos/state/initiatives', 'state/initiatives'];
|
|
169
|
+
/** { [repo-relative path]: Set<id> } — each record and every `*_path` its frontmatter names. */
|
|
170
|
+
function documentOwners(root, ids, recordsDir) {
|
|
171
|
+
const fs = require('fs');
|
|
172
|
+
const path = require('path');
|
|
173
|
+
const dirs = recordsDir ? [recordsDir] : RECORD_DIRS.map((d) => path.join(root, d));
|
|
174
|
+
const owners = new Map();
|
|
175
|
+
const add = (rel, id) => {
|
|
176
|
+
const k = String(rel || '').trim().replace(/^["']|["']$/g, '').replace(/^\.?\//, '');
|
|
177
|
+
if (!k || /\s/.test(k) || k.includes('..')) return;
|
|
178
|
+
if (!owners.has(k)) owners.set(k, new Set());
|
|
179
|
+
owners.get(k).add(id);
|
|
180
|
+
};
|
|
181
|
+
for (const id of ids) {
|
|
182
|
+
for (const dir of dirs) {
|
|
183
|
+
const file = path.join(dir, id + '.md');
|
|
184
|
+
let text;
|
|
185
|
+
try { text = fs.readFileSync(file, 'utf8'); } catch { continue; }
|
|
186
|
+
add(path.relative(root, file).split(path.sep).join('/'), id);
|
|
187
|
+
const fm = /^---\r?\n([\s\S]*?)\r?\n---/.exec(text);
|
|
188
|
+
if (fm) for (const m of fm[1].matchAll(/^[a-z_]*_path[ \t]*:[ \t]*(\S.*?)[ \t]*$/gm)) add(m[1], id);
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return owners;
|
|
193
|
+
}
|
|
194
|
+
function changeSignal(root, head, known, opts) {
|
|
195
|
+
const out = new Set();
|
|
196
|
+
if (!known.length) return out;
|
|
197
|
+
const owners = documentOwners(root, known, opts && opts.recordsDir);
|
|
198
|
+
const ids = new Set(known);
|
|
199
|
+
const r = gitCap(root, ['log', '--first-parent', '--diff-merges=first-parent', '--format=%x1e%s%x1f', '--name-only', head]);
|
|
200
|
+
if (!r.ok) return out;
|
|
201
|
+
for (const chunk of r.out.split('\x1e')) {
|
|
202
|
+
if (!chunk.trim()) continue;
|
|
203
|
+
const cut = chunk.indexOf('\x1f');
|
|
204
|
+
const subject = cut < 0 ? chunk : chunk.slice(0, cut);
|
|
205
|
+
const files = cut < 0 ? [] : chunk.slice(cut + 1).split('\n').map((s) => s.trim()).filter(Boolean);
|
|
206
|
+
if (!files.some(isDeliveryPath)) continue;
|
|
207
|
+
const touched = new Set();
|
|
208
|
+
for (const f of files) for (const id of owners.get(f) || []) touched.add(id);
|
|
209
|
+
if (touched.size && touched.size <= MAX_CO_INITIATIVES) for (const id of touched) out.add(id);
|
|
210
|
+
const b = /^Merge (?:pull request #\d+ from|branch ')\s*(?:[^/\s]+\/)?(?:claude|feature|feat|fix|chore)\/([A-Za-z0-9][\w.-]*?)'?(?:\s|$)/.exec(subject.trim());
|
|
211
|
+
if (b) {
|
|
212
|
+
const slug = b[1];
|
|
213
|
+
const bare = slug.replace(/-[a-z0-9]{4,8}$/, '');
|
|
214
|
+
if (ids.has(slug)) out.add(slug);
|
|
215
|
+
else if (bare !== slug && ids.has(bare)) out.add(bare);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return out;
|
|
219
|
+
}
|
|
220
|
+
|
|
145
221
|
/*
|
|
146
222
|
* The branches that actually MERGED — a strictly narrower signal than `byBranch`, and the two are
|
|
147
223
|
* kept apart on purpose.
|
|
@@ -251,12 +327,13 @@ function declaredDelivery(root, declared) {
|
|
|
251
327
|
* caller that collapses the two reports "nothing was delivered" for a repository whose history it
|
|
252
328
|
* failed to read, which is a claim the missing evidence cannot support.
|
|
253
329
|
*
|
|
254
|
-
* @returns {'branch'|'mention'|'unknown'|null}
|
|
330
|
+
* @returns {'branch'|'mention'|'change'|'unknown'|null}
|
|
255
331
|
*/
|
|
256
332
|
function attribution(index, id) {
|
|
257
333
|
if (!index || !index.ok) return 'unknown';
|
|
258
334
|
if (index.byBranch.has(id)) return 'branch';
|
|
259
335
|
if (index.byMention.has(id)) return 'mention';
|
|
336
|
+
if (index.byChange && index.byChange.has(id)) return 'change';
|
|
260
337
|
return null;
|
|
261
338
|
}
|
|
262
339
|
|
|
@@ -264,6 +341,7 @@ function attribution(index, id) {
|
|
|
264
341
|
function attributionEvidence(signal, id, extra) {
|
|
265
342
|
if (signal === 'branch') return `an initiative/${id} branch merged to the trunk`;
|
|
266
343
|
if (signal === 'mention') return 'a commit on the trunk names this initiative and changes build files';
|
|
344
|
+
if (signal === 'change') return 'a change merged to the trunk carried this initiative\'s own documents and build files together';
|
|
267
345
|
if (signal === 'declared') return `delivered_by ${String(extra || '').slice(0, 8)} — verified on the trunk, and it changes build paths`;
|
|
268
346
|
if (signal === 'unknown') return 'the trunk history could not be read — delivery cannot be derived here';
|
|
269
347
|
return 'no commit on the trunk is attributable to this initiative';
|
|
@@ -274,5 +352,5 @@ function _resetCache() { deliveryCache.clear(); }
|
|
|
274
352
|
|
|
275
353
|
module.exports = {
|
|
276
354
|
deliveryIndex, attribution, attributionEvidence, declaredDelivery, mergedBranches,
|
|
277
|
-
isDeliveryPath, trunkRef, _resetCache,
|
|
355
|
+
isDeliveryPath, trunkRef, documentOwners, _resetCache,
|
|
278
356
|
};
|
|
@@ -438,7 +438,7 @@ function buildCommits(root, range, lane, opts) {
|
|
|
438
438
|
if (declared && declared.declared && !declared.ok) {
|
|
439
439
|
return { unknown: false, failedDeclaration: declared, first: null, count: 0, sample: null, signal: null, attributed: true };
|
|
440
440
|
}
|
|
441
|
-
const index = (opts && opts.deliveryIndex) || delivery.deliveryIndex(root, listInitiatives(root));
|
|
441
|
+
const index = (opts && opts.deliveryIndex) || delivery.deliveryIndex(root, listInitiatives(root), { recordsDir: layout(root).initiatives });
|
|
442
442
|
signal = delivery.attribution(index, id);
|
|
443
443
|
if (declared && declared.ok) {
|
|
444
444
|
// A verified declaration attributes the work regardless of what the conventions found. It is
|
|
@@ -478,7 +478,7 @@ function buildCommits(root, range, lane, opts) {
|
|
|
478
478
|
count++;
|
|
479
479
|
if (first === null) { first = i; sample = hit; }
|
|
480
480
|
}
|
|
481
|
-
if (whole && id && signal === 'branch' && first === null) {
|
|
481
|
+
if (whole && id && (signal === 'branch' || signal === 'change') && first === null) {
|
|
482
482
|
// The branch merged and every commit it carried was written on the branch, so no trunk commit
|
|
483
483
|
// subject names the id. The merge itself is the evidence, and saying "0 commits" over a merged
|
|
484
484
|
// branch would be the under-claim in a new place.
|
|
@@ -1657,7 +1657,7 @@ function main(argv) {
|
|
|
1657
1657
|
// reason the commit range is: `deliveryIndex` memoises per trunk head, so a per-initiative build
|
|
1658
1658
|
// would be cheap but the intent — one read, shared — is what keeps the rows consistent with each
|
|
1659
1659
|
// other even if the trunk moved mid-run.
|
|
1660
|
-
const deliveryIdx = delivery.deliveryIndex(root, ids);
|
|
1660
|
+
const deliveryIdx = delivery.deliveryIndex(root, ids, { recordsDir: layout(root).initiatives });
|
|
1661
1661
|
const rows = ids.map((i) => computeWorkflow(i, { root, range, changeRange, deliveryIndex: deliveryIdx }));
|
|
1662
1662
|
if (json) { process.stdout.write(JSON.stringify(rows, null, 2) + '\n'); return 0; }
|
|
1663
1663
|
if (!rows.length) { process.stdout.write('\n no initiative records found — is this a PMOS repo?\n\n'); return 1; }
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Discoveries and friction log"
|
|
4
4
|
description: "Append-only record of what runs discovered and where the process rubbed."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-06T15:12:25Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
package/workspace/REMOTE.md
CHANGED
|
@@ -217,7 +217,7 @@ Run these from a machine that is **not** signed in to Access:
|
|
|
217
217
|
- **`403` "the public host is only reachable through Cloudflare Access"** — the request reached the workspace without passing Access. Create the Access application for the hostname (Setup step 3).
|
|
218
218
|
- **`403` "forbidden origin/host" on the public hostname** — `PMOS_PUBLIC_HOST` is not set, or it names another hostname, or the tunnel rewrites the `Host` header. Remove `httpHostHeader` from the tunnel's ingress rule.
|
|
219
219
|
- **A local reverse proxy now gets `401`** — it adds a forwarding header, so its requests need the token. This is intended.
|
|
220
|
-
- **`403` "your Cloudflare sign-in could not be verified for this workspace"** (Online switch) — check the AUD tag, the team domain and the email in **Settings › Online**. The email must be the one you sign in with.
|
|
220
|
+
- **`403` "your Cloudflare sign-in could not be verified for this workspace"** (Online switch) — check the AUD tag, the team domain and the email in **Settings › Online**. The email must be the one you sign in with. Settings › Online on the desktop names the check that refused the last sign-in. To see which email Cloudflare signed you in with, open `https://<your address>/cdn-cgi/access/get-identity` and read `email`.
|
|
221
221
|
- **Cloudflare error 1033** — the tunnel is not running. In **Settings › Online**, check the status line, and read `online-tunnel.log` in the workspace state folder. Another `cloudflared` for the same tunnel (for example a service you installed) also serves the hostname.
|
|
222
222
|
|
|
223
223
|
- **"refusing to bind … only loopback or a Tailscale tailnet address (100.64.0.0/10) is permitted"** — you passed `PMOS_BIND_ADDR` (or `PMOS_TAILNET_IP`) that isn't loopback or tailnet. Use your real `tailscale ip -4` value.
|
|
@@ -54,27 +54,32 @@
|
|
|
54
54
|
{
|
|
55
55
|
"id": "intake",
|
|
56
56
|
"label": "Intake",
|
|
57
|
-
"state": "done"
|
|
57
|
+
"state": "done",
|
|
58
|
+
"owed": null
|
|
58
59
|
},
|
|
59
60
|
{
|
|
60
61
|
"id": "spec",
|
|
61
62
|
"label": "Spec",
|
|
62
|
-
"state": "done"
|
|
63
|
+
"state": "done",
|
|
64
|
+
"owed": null
|
|
63
65
|
},
|
|
64
66
|
{
|
|
65
67
|
"id": "build",
|
|
66
68
|
"label": "Build",
|
|
67
|
-
"state": "current"
|
|
69
|
+
"state": "current",
|
|
70
|
+
"owed": null
|
|
68
71
|
},
|
|
69
72
|
{
|
|
70
73
|
"id": "evaluate",
|
|
71
74
|
"label": "Evaluate",
|
|
72
|
-
"state": "todo"
|
|
75
|
+
"state": "todo",
|
|
76
|
+
"owed": null
|
|
73
77
|
},
|
|
74
78
|
{
|
|
75
79
|
"id": "accept",
|
|
76
80
|
"label": "Accept",
|
|
77
|
-
"state": "todo"
|
|
81
|
+
"state": "todo",
|
|
82
|
+
"owed": null
|
|
78
83
|
}
|
|
79
84
|
],
|
|
80
85
|
"waiting": {
|
|
@@ -90,14 +95,31 @@
|
|
|
90
95
|
{
|
|
91
96
|
"kind": "prd",
|
|
92
97
|
"label": "PRD",
|
|
93
|
-
"path": "planning/prd/partner-sso.md"
|
|
98
|
+
"path": "planning/prd/partner-sso.md",
|
|
99
|
+
"source": "record"
|
|
94
100
|
},
|
|
95
101
|
{
|
|
96
102
|
"kind": "rubric",
|
|
97
103
|
"label": "Rubric",
|
|
98
|
-
"path": "planning/evals/partner-sso.md"
|
|
104
|
+
"path": "planning/evals/partner-sso.md",
|
|
105
|
+
"source": "record"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"kind": "explainer",
|
|
109
|
+
"label": "Explainer",
|
|
110
|
+
"path": "planning/explainers/partner-sso-surface-explainer.md",
|
|
111
|
+
"source": "knowledge-graph",
|
|
112
|
+
"shared": false
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"kind": "code",
|
|
116
|
+
"label": "Code",
|
|
117
|
+
"path": "workspace/lib/partner-sso.js",
|
|
118
|
+
"source": "git",
|
|
119
|
+
"commits": 2
|
|
99
120
|
}
|
|
100
121
|
],
|
|
122
|
+
"derived": null,
|
|
101
123
|
"history": [
|
|
102
124
|
{
|
|
103
125
|
"at": "2026-10-04T09:00:00Z",
|
package/workspace/guide/index.md
CHANGED
|
@@ -13,15 +13,15 @@ This guide describes the PMOS workspace as it works today. It ships with the app
|
|
|
13
13
|
On a desktop the workspace is one frame. [Layout](layout.md) explains each part.
|
|
14
14
|
|
|
15
15
|
1. **The top bar.** Repository ▾, the five places (Docs included), search (⌘K), Sync when the repository is not in sync, and Settings.
|
|
16
|
-
2. **The place.** One of
|
|
16
|
+
2. **The place.** One of Agenda, Initiatives, Board or Reports, or Docs (⌘K or `g d`). It fills the screen under the bar.
|
|
17
17
|
3. **The detail.** The item you opened: an initiative with its status, next steps and artifacts, or the profiles. It opens beside the place and closes with ×.
|
|
18
|
-
4. **The agent dock.** Terminals, documents, conversations and web pages. It is open on
|
|
18
|
+
4. **The agent dock.** Terminals, documents, conversations and web pages. It is open on Agenda and a thin rail elsewhere.
|
|
19
19
|
|
|
20
20
|
## Where things are
|
|
21
21
|
|
|
22
22
|
| You want | Go to |
|
|
23
23
|
|---|---|
|
|
24
|
-
| What is waiting for you now |
|
|
24
|
+
| What is waiting for you now | Agenda |
|
|
25
25
|
| The initiatives of a repository, or of every repository | Initiatives. Choose **All repositories** in Repository ▾ for every repository. |
|
|
26
26
|
| The sessions of a repository, by initiative | The agent dock. ⌘K → **Branches and sessions** lists the branches. |
|
|
27
27
|
| The items of an initiative | Board, or Breakdown in the initiative detail |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Guide
|
|
3
3
|
title: "Initiatives: the Roadmap, the List and the initiative sheet"
|
|
4
4
|
description: "How to read and order the initiatives of a repository on the Roadmap and the List, open the initiative sheet, make a new initiative, and review the cycle."
|
|
5
|
-
timestamp: 2026-10-
|
|
5
|
+
timestamp: 2026-10-06
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Initiatives: the Roadmap, the List and the initiative sheet
|
|
@@ -11,6 +11,12 @@ The Initiatives place shows every initiative of the selected repository. Each in
|
|
|
11
11
|
|
|
12
12
|
Press **Roadmap** or **List** at the top to change the view. The workspace keeps your choice in this browser.
|
|
13
13
|
|
|
14
|
+
### Re-scan
|
|
15
|
+
|
|
16
|
+
The workspace reads the stage, the files and the breakdown of each initiative from the repository. It reads them again when an initiative record changes, and at the latest after ten minutes. A change that does not touch a record, for example a pull request merged on the trunk, can show late.
|
|
17
|
+
|
|
18
|
+
To read the repository again now, use **Re-scan the repository**. It is in the **⋯** menu of an initiative sheet, and in the command menu (⌘K). A toast shows **Re-scanning…**, and another shows the time when the read ends. Then the view updates. Re-scan writes nothing. It works the same on PMOS itself and on a tenant repository.
|
|
19
|
+
|
|
14
20
|
## The Roadmap
|
|
15
21
|
|
|
16
22
|
The Roadmap has three columns: Now, Next and Later. Each column is a ranked list of cards. The line at the top reads, for example, "Now · 5 KR · 3 health". It counts the initiatives in Now by what they serve: a key result (KR) or a health budget. When the rules set an advisory cap for Now, the line shows it. The cap only advises. It never stops a move.
|
|
@@ -56,6 +62,24 @@ From the top:
|
|
|
56
62
|
|
|
57
63
|
An initiative that is finished reads "Accepted". An initiative on hold, closed or cancelled reads that word instead of a stage. After 5 days with no activity, the line adds "idle 5 d".
|
|
58
64
|
|
|
65
|
+
### How the stage is read
|
|
66
|
+
|
|
67
|
+
The stage is the first stage with an open step at or after the furthest stage that has evidence. Work that went past a step, for example a build that merged before a contract was logged, is not held at the earlier stage. The earlier stage shows a warning mark, and its tooltip names the step that is still open, for example "Skipped: contract is still open". An accepted initiative has no current stage.
|
|
68
|
+
|
|
69
|
+
The build counts as done when the trunk holds evidence for this initiative: a merged `initiative/<id>` branch, a commit that names the id and changes build files, or a merged change that changed this initiative's own documents (its record, or a file the record names) and build files together. A change that touched the documents of more than two initiatives is not evidence for any of them.
|
|
70
|
+
|
|
71
|
+
### The Files tab
|
|
72
|
+
|
|
73
|
+
The Files tab lists every file that belongs to the initiative, in three groups:
|
|
74
|
+
|
|
75
|
+
- **Named by the record.** The record, and each file its frontmatter names with a key that ends in `_path`: `prd_path`, `rubric_path`, `explainer_path`, `design_path` and others. A file the record names that does not exist is shown as missing.
|
|
76
|
+
- **Linked documents.** The documents the library joins to the initiative: a document that declares its `initiative_id`, a document the record names, a file name that starts with the id.
|
|
77
|
+
- **Changed for it in git.** The files that the commits for this initiative changed and that still exist. These are its own branch, trunk commits that name the id, and merged changes that carried its documents. The file opens read-only.
|
|
78
|
+
|
|
79
|
+
### A breakdown read from the spec
|
|
80
|
+
|
|
81
|
+
When nobody has planned items for an initiative, the Breakdown tab shows the items of its spec above the empty Breakdown: the user stories, or the entries of the Requirements section of the PRD that the record names. Each item shows the state of the whole initiative: **Done** after you accept it, **Built** when its build is on the trunk, and **Todo** before that. These items are read-only and nothing is written. To track an item, add it to the Breakdown. The card and the row show the same count, for example "0 of 8 · from board-delivery-truth-prd.md".
|
|
82
|
+
|
|
59
83
|
The More button in the sheet has:
|
|
60
84
|
|
|
61
85
|
- **Move to Now**, **Move to Next**, **Move to Later**.
|
|
@@ -15,7 +15,7 @@ The top bar has these parts, from left to right:
|
|
|
15
15
|
|
|
16
16
|
- **Repository ▾** names the current repository. Press it to choose another repository, or **All repositories** to see the work of every repository at once. All repositories replaces the old Portfolio mode.
|
|
17
17
|
- **Branches and sessions** is in ⌘K. It opens a sheet with every branch of the repository (main first) and how many sessions run in each. Press a branch to see its sessions, open one, end one, or start a session there. **+ branch** makes a branch, and **+ session** starts one with a branch, an agent and a profile. With All repositories, the sheet lists every repository.
|
|
18
|
-
- **
|
|
18
|
+
- **Agenda, Initiatives, Board, Reports and Docs** are the five places. Press one to show it. The Agenda count shows how many items need you. Docs shows whether each document is the same as its Google Docs and Confluence copy.
|
|
19
19
|
- **Search or capture ⌘K** opens the command palette. Type to find an initiative, a repository, an open tab or a command.
|
|
20
20
|
- **Sync** shows only when the current repository is not in sync with its remote. Press it to fetch, bring in the work of the other machine, record your changes and push. If the repository has not fetched in the last 10 minutes, Sync also shows, because a push from another machine is not visible until a fetch. When the repository fetched recently and is in sync, Sync is not shown. If Sync stops, the message names the step that stopped.
|
|
21
21
|
- **Sync on its own.** If you forget to press Sync, the workspace syncs for you. It syncs each repository once when the workspace starts. It also syncs a repository that has records not yet pushed when no record changed for 5 minutes. In a document repository it syncs every changed file on any branch. In a code repository it syncs only on the default branch (for example `main`), and only when every commit not yet pushed changes records and no code. If you close the tab while records are not yet pushed, the browser asks you first. To turn automatic sync off, start the workspace with `PMOS_AUTO_SYNC=0`.
|
|
@@ -31,17 +31,17 @@ Settings holds settings only, in seven sections:
|
|
|
31
31
|
- **Profiles**: the Claude profiles a session can start with. They open in the detail.
|
|
32
32
|
- **New sessions**: the default CLI and the default model for a new session in the dock.
|
|
33
33
|
- **Validate consent**: the consent of the current repository. Turn it on or off here. **Open Validate** opens the Validate page as a tab.
|
|
34
|
-
- **Online**: puts this workspace at your own address, such as `pmos.pm-os.app`, through a Cloudflare tunnel. Enter the public address, the tunnel name, your Cloudflare team domain, the Access audience (AUD) tag and your email, then press **Turn on**. The workspace runs the tunnel and remembers the switch when it restarts. On your phone, open the address and sign in with Cloudflare Access (Google or an email code). The workspace does not ask for its token there. Only the computer that runs the workspace can change this section. A phone sees whether it is on. [REMOTE.md](../REMOTE.md) has the one-time Cloudflare steps.
|
|
34
|
+
- **Online**: puts this workspace at your own address, such as `pmos.pm-os.app`, through a Cloudflare tunnel. Enter the public address, the tunnel name, your Cloudflare team domain, the Access audience (AUD) tag and your email, then press **Turn on**. The workspace runs the tunnel and remembers the switch when it restarts. On your phone, open the address and sign in with Cloudflare Access (Google or an email code). The workspace does not ask for its token there. Only the computer that runs the workspace can change this section. A phone sees whether it is on. When a sign-in at the address is refused, this section names the check that failed, such as the email or the AUD tag. [REMOTE.md](../REMOTE.md) has the one-time Cloudflare steps.
|
|
35
35
|
- **Theme**: Dark or Light.
|
|
36
36
|
- **Updates**: the version of the workspace app and the version of the kit in the current repository. Press **Check for updates** to compare them with the latest published version of PMOS. This is the only time the workspace asks the internet for a version, and it asks only when you press. When a newer version is available, a button shows for each part that is older:
|
|
37
37
|
- **Update the kit** does what `npx create-pmos@latest --update` does in a terminal. It changes only the kit's own files under `pmos/` (skills, scripts, templates and guides). Your records in `state/`, `planning/` and `okf/product/` do not change. The last lines of the output show under the button. The changed files are under `pmos/`, which Sync treats as records, so Sync (or automatic sync) sends them with your other records. To read them first, look at `git diff pmos/` before you sync.
|
|
38
38
|
- **Update the workspace** does what `pmos upgrade` does in a terminal. It asks you first. The workspace stops, installs the new version and starts again, and the page reloads when the new version starts. Sessions keep running in tmux. Press it on the computer that runs the workspace. From a phone it is refused, because the phone cannot see the restart through. When the workspace runs from a PMOS checkout, update it with `git pull` instead.
|
|
39
39
|
|
|
40
|
-
Your work is never in Settings. It is in
|
|
40
|
+
Your work is never in Settings. It is in Agenda, Initiatives, Board and Reports.
|
|
41
41
|
|
|
42
42
|
## The agent dock
|
|
43
43
|
|
|
44
|
-
The dock holds your terminals, documents, conversations, skills and web pages. It is open on
|
|
44
|
+
The dock holds your terminals, documents, conversations, skills and web pages. It is open on Agenda. On every other place it is a 44-pixel rail at the right edge.
|
|
45
45
|
|
|
46
46
|
- Press ⌘J (Ctrl-J), or the button on the rail, to open the dock. Press ⌘J again, or the fold button in the dock, to fold it to the rail.
|
|
47
47
|
- The dock never opens by itself. A tab that opens while the dock is a rail waits in the dock until you open it.
|
|
@@ -67,13 +67,13 @@ When the workspace needs an answer from you, for example the name for a rename o
|
|
|
67
67
|
|
|
68
68
|
## Colour and size
|
|
69
69
|
|
|
70
|
-
The workspace is neutral. A primary button is the text colour on the page colour, in both themes. The one brand colour marks the
|
|
70
|
+
The workspace is neutral. A primary button is the text colour on the page colour, in both themes. The one brand colour marks the Agenda dot and the Agenda count, and nothing else. The bar, the dock and the bottom bar follow the theme; only a terminal stays dark. Corners are square. Controls are icons from one set, not text symbols. On a touch screen every field is 16 pixels, so the phone does not zoom when you tap it, and every control is at least 44 pixels high.
|
|
71
71
|
|
|
72
72
|
## Keys
|
|
73
73
|
|
|
74
74
|
| Key | What it does |
|
|
75
75
|
|---|---|
|
|
76
|
-
| g t, g i, g b, g r, g d | Show
|
|
76
|
+
| g t, g i, g b, g r, g d | Show Agenda, Initiatives, Board, Reports or Docs. |
|
|
77
77
|
| ⌘K (Ctrl-K) | Open or close the command palette. |
|
|
78
78
|
| ⌘J (Ctrl-J) | Open or fold the dock. |
|
|
79
79
|
| ⌘⇧J (Ctrl-Shift-J) | Focus: give the dock the whole window, or show the place again. |
|
|
@@ -89,14 +89,12 @@ A key that you type in a field, or in a terminal, goes to the field or the termi
|
|
|
89
89
|
|
|
90
90
|
## On a phone
|
|
91
91
|
|
|
92
|
-
A phone shows one panel at a time. The tabs at the bottom are **
|
|
92
|
+
A phone shows one panel at a time. The tabs at the bottom are **Agenda, Initiatives, Board, Reports and Agent**. Agent shows the dock, with your terminals. Search or capture ⌘K is pinned at the top of Agenda.
|
|
93
93
|
|
|
94
94
|
The bar at the top has Back (when you are deeper than a tab), Repository, the name of the screen, ⌘K, Sync (when the repository is not in sync, or has not fetched in the last 10 minutes) and Settings. Settings opens over the current tab. Back returns to the tab.
|
|
95
95
|
|
|
96
96
|
Every row has a visible ⋯ button. Every control is at least 44 pixels high. Nothing works only when you hover.
|
|
97
97
|
|
|
98
|
-
When the keyboard opens or closes, or you turn the phone, the terminal fits itself to the new space. The session is told its new size when the keyboard stops moving, not at every step, so an agent redraws its screen once. Scrolling the page does not resize the terminal.
|
|
99
|
-
|
|
100
98
|
## The older layout
|
|
101
99
|
|
|
102
100
|
The address options `?layout=legacy` and `?fixture=cockpit` keep the older layouts. The rest of this page describes them. In the cockpit, the workspace has four columns: the repository rail, the list, the detail and the tabs. With `?layout=legacy` it has three columns: navigation, detail and tabs.
|
|
@@ -180,6 +180,7 @@ Text that you type in a setting (the name, the stalled days, the quiet hours) or
|
|
|
180
180
|
- `pmos proactive settings` shows the settings. Add `--enabled`, `--trigger NAME=on|off`, `--stalled-days`, `--quiet HH:MM-HH:MM|none`, `--pause-learning`, `--background-prepare on|off`, `--brief-source NAME=on|off`, `--scope SERVICE:NAME=watch|search|ignore`, `--day-check 30|60|off`, `--website URL`, `--no-website URL`, `--brief-scoring on|off` or `--name` to change one.
|
|
181
181
|
- `pmos proactive goals` lists today's goals with their steps. `pmos proactive goals add TEXT [--initiative ID]` adds one. `done N`, `drop N`, `reopen N` and `remove N` change the Nth goal. `pmos proactive goals plan` does the same as **Prepare my day**.
|
|
182
182
|
- `pmos proactive brief` shows today's brief. `pmos proactive brief submit --file PATH` submits one. `pmos proactive brief submit --file PATH --merge` merges one into the stored brief of the same day. `pmos proactive brief schedule` makes the 06:45 schedule for the current repository.
|
|
183
|
+
- `pmos proactive calendar` shows what the calendar memory holds (the number of events, the days it covers and when it was read) and the window the next morning brief reads: 90 days back and 31 ahead when the memory is empty or older than 7 days, else from today to 31 days ahead. It only reads.
|
|
183
184
|
- `pmos proactive day` shows the stage of the day and its tasks. `pmos proactive day show TASK` prints one task with its item, its draft and the notes of each revision. Sessions read it.
|
|
184
185
|
- `pmos proactive day deliver TASK --summary TEXT --body-file PATH [--link URL]` delivers the draft of a working task. `pmos proactive day sent TASK [--link URL]` reports that the approved step was done. `pmos proactive day fail TASK --reason TEXT` reports that a task cannot be done. Sessions run these three.
|
|
185
186
|
- `pmos proactive day approve TASK`, `pmos proactive day revise TASK --note TEXT` and `pmos proactive day retry TASK` are your decisions, the same as the buttons. `pmos proactive day check` starts a check now.
|
package/workspace/guide/today.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
type: Guide
|
|
3
3
|
title: "Today: what needs you"
|
|
4
|
-
description: "The two-column day (your jobs and the agent's jobs), the eight kinds of card on Today, the one button on each card, the count, the empty state, and the pmos today command."
|
|
4
|
+
description: "The Agenda month and the calendar it remembers, the two-column day (your jobs and the agent's jobs), the eight kinds of card on Today, the one button on each card, the count, the empty state, and the pmos today command."
|
|
5
5
|
timestamp: 2026-10-06
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -32,6 +32,28 @@ Each job session is named "Job ·" and the first words of the job, so the sessio
|
|
|
32
32
|
|
|
33
33
|
**Agent results**, at the top of the left column, counts the jobs and lists each finished one with its draft summary or the reason it failed. Your goals for today come under it, above the calendar. The other things that wait for you, such as outcome checks, report drafts and sessions, are in **Also waiting for you** at the end of the left column. When the day is folded, they show under it.
|
|
34
34
|
|
|
35
|
+
### The Agenda month
|
|
36
|
+
|
|
37
|
+
The menu calls this page **Agenda**. At the top of the left column is the month, with today marked. Each week starts with its number, for example W41.
|
|
38
|
+
|
|
39
|
+
- Press a day to see its agenda under the month. Three tiles open the agenda: the time in meetings, the meeting that matters most, and your prep.
|
|
40
|
+
- Press a week number to see the agenda of that week, day by day.
|
|
41
|
+
- Press a meeting to see its memory in the right column. The month marks every day of the same meeting, so you see when it happens again.
|
|
42
|
+
|
|
43
|
+
Meetings that repeat and have nothing for you fold into one line, for example "+4 repeating, nothing for you". Press it to see them.
|
|
44
|
+
|
|
45
|
+
Each day of the month answers three questions:
|
|
46
|
+
|
|
47
|
+
- **How full is the day?** The bar covers 09:00 to 18:00. Each block is a meeting. A dark block is an important meeting.
|
|
48
|
+
- **What matters?** The ★ line names the one meeting of the day that matters. A meeting matters when you present or decide, when people from outside the company or leadership join, or when 8 people or more join. A meeting whose prep is at risk also shows.
|
|
49
|
+
- **How ready are you?** The badge shows the prep steps done out of the steps needed, for example 1/3. It is red when the free time before the meeting is shorter than 30 minutes for each step left. A past day shows how many one-to-ones and important meetings have no notes.
|
|
50
|
+
|
|
51
|
+
The switch above the month shows one answer at a time: **Overview** (all three), **Full** (the time in meetings), **Key** (the meeting that matters), **Prep** (how ready you are) or **Free** (the free blocks of 90 minutes or more).
|
|
52
|
+
|
|
53
|
+
The memory of a meeting says what kind of meeting it is (one-to-one, onboarding or group), how many times it was held, how many are to come, and why it matters. **Before this meeting** lists the prep. **Last time** is folded: open it to read the notes and what stayed open.
|
|
54
|
+
|
|
55
|
+
The month comes from the calendar memory. The morning-brief session reads your calendar 90 days back and 31 days ahead the first time, and from today to 31 days ahead after that, and sends it with the brief. The workspace keeps 100 days back and 45 days ahead, in `calendar-memory.json` in the state folder. Nothing on the Agenda writes to your calendar. To see what the memory holds, run `pmos proactive calendar`.
|
|
56
|
+
|
|
35
57
|
### One day for every repository
|
|
36
58
|
|
|
37
59
|
The day is yours, not a repository's. Every repository shows the same calendar, goals, jobs and mentions. To narrow the day, use the **Show** switch at the top of Today:
|