create-pmos 0.46.0 → 0.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/package.json +1 -1
  2. package/payload/AGENTS-HUMAN.md +1 -1
  3. package/payload/AGENTS.md +1 -1
  4. package/payload/KIT-VERSION +1 -1
  5. package/payload/mods/pmos-cockpit/.claude-plugin/plugin.json +1 -1
  6. package/payload/mods/pmos-cockpit/README.md +13 -3
  7. package/payload/mods/pmos-cockpit/hooks/lib/commands.mjs +34 -6
  8. package/payload/mods/pmos-cockpit/hooks/lib/grants.mjs +22 -3
  9. package/payload/mods/pmos-cockpit/hooks/lib/guard.mjs +16 -3
  10. package/payload/mods/pmos-cockpit/hooks/lib/handover-run.mjs +33 -0
  11. package/payload/mods/pmos-cockpit/hooks/lib/handover.mjs +267 -0
  12. package/payload/mods/pmos-cockpit/hooks/register.tsx +189 -18
  13. package/payload/mods/pmos-cockpit/types/index.d.ts +16 -1
  14. package/payload/okf/core/concepts/frontmatter-format.md +6 -1
  15. package/payload/planning/evals/eval-calibration.md +1 -1
  16. package/payload/planning/okrs/health-budgets.md +1 -1
  17. package/payload/scripts/pmos-log.sh +18 -8
  18. package/payload/scripts/pmos-model.js +233 -2
  19. package/payload/scripts/status-facts.js +204 -0
  20. package/payload/scripts/workflow-state.js +52 -0
  21. package/payload/skills/SKILL-FORMAT.md +1 -0
  22. package/payload/state/discoveries.md +1 -1
  23. package/workspace/README.md +3 -3
  24. package/workspace/bin/live-notes.js +133 -0
  25. package/workspace/design-parity.js +328 -21
  26. package/workspace/doc-category-bench.js +165 -0
  27. package/workspace/fixtures/doc-category-labelled.json +167 -0
  28. package/workspace/guide/initiatives-and-next-steps.md +16 -2
  29. package/workspace/guide/knowledge-graph.md +89 -2
  30. package/workspace/guide/layout.md +30 -2
  31. package/workspace/guide/proactive-engine.md +26 -4
  32. package/workspace/guide/scheduled-tasks-and-cli.md +1 -0
  33. package/workspace/guide/statuses.md +1 -0
  34. package/workspace/guide/validate-with-typesafe.md +17 -1
  35. package/workspace/lib/claims.js +51 -2
  36. package/workspace/lib/doc-category-corrections.js +113 -0
  37. package/workspace/lib/doc-category-writeback.js +110 -0
  38. package/workspace/lib/doc-category.js +225 -0
  39. package/workspace/lib/engine/pmos-model.js +233 -2
  40. package/workspace/lib/engine/status-facts.js +204 -0
  41. package/workspace/lib/engine/workflow-state.js +52 -0
  42. package/workspace/lib/jev-categorize.js +399 -0
  43. package/workspace/lib/jev-claims.js +59 -15
  44. package/workspace/lib/jev-route.js +129 -0
  45. package/workspace/lib/kg-cli.js +5 -2
  46. package/workspace/lib/kg-dangling-baseline.json +1 -0
  47. package/workspace/lib/kg-dangling-rules.json +1 -0
  48. package/workspace/lib/kg.js +26 -9
  49. package/workspace/lib/learn.js +282 -0
  50. package/workspace/lib/live-notes.js +890 -0
  51. package/workspace/lib/proactive-cli.js +49 -1
  52. package/workspace/lib/proactive.js +8 -7
  53. package/workspace/lib/typesafe-settings.js +10 -2
  54. package/workspace/package-lock.json +1 -1
  55. package/workspace/package.json +5 -4
  56. package/workspace/public/pm/app.js +148 -79
  57. package/workspace/public/pm/data.js +58 -0
  58. package/workspace/public/pm/fixture.js +40 -2
  59. package/workspace/public/pm/modes-live.js +267 -21
  60. package/workspace/public/pm/panel-cockpit.js +636 -0
  61. package/workspace/public/pm/panel-detail.js +210 -8
  62. package/workspace/public/pm/panel-initiatives.js +9 -9
  63. package/workspace/public/pm/panel-newsess.js +145 -86
  64. package/workspace/public/pm/panel-repos.js +34 -17
  65. package/workspace/public/pm/panel-tabs.js +145 -27
  66. package/workspace/public/pm/state.js +45 -4
  67. package/workspace/public/pm/tab-doc.js +244 -16
  68. package/workspace/public/pm/tab-term.js +11 -1
  69. package/workspace/public/pm/tokens.css +4 -3
  70. package/workspace/public/pm/view-addrepo.js +207 -0
  71. package/workspace/public/pm/view-files.js +197 -0
  72. package/workspace/public/pm/view-guide.js +141 -0
  73. package/workspace/public/pm/view-knowledge.js +225 -0
  74. package/workspace/public/pm/view-memory.js +122 -0
  75. package/workspace/public/pm/view-portfolio.js +153 -0
  76. package/workspace/public/pm/view-reports.js +190 -0
  77. package/workspace/public/pm/view-scheduled.js +161 -0
  78. package/workspace/public/pm/view-skills.js +216 -0
  79. package/workspace/public/pm/view-today.js +168 -0
  80. package/workspace/public/pm/view-validate.js +171 -0
  81. package/workspace/server.js +394 -10
  82. package/workspace/skills/live-notes.skill +58 -0
  83. package/workspace/skills/morning-brief.skill +1 -0
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env node
2
+ /* doc-category-bench.js — scores lib/doc-category.js against the labelled set and counts what the repository leaves undecided
3
+ * (jev-doc-categorizer Run 1). Local only: no network, no model, nothing written.
4
+ *
5
+ * node doc-category-bench.js print the report
6
+ * node doc-category-bench.js --json print it as JSON
7
+ *
8
+ * node doc-category-bench.js --jev Run 3's measured pass: show how many snippets of the labelled set
9
+ * would go to Jev, and send nothing
10
+ * node doc-category-bench.js --jev --confirm N send them, with the PM's own key, and report the agreement
11
+ *
12
+ * The measured pass is the PM's own press, never the build's or CI's. It uses the key, the consent and the "categorize with
13
+ * Jev" switch the PM set in the workspace for this repository (D82), and it refuses unless N is the count it shows now.
14
+ *
15
+ * The labelled set is workspace/fixtures/doc-category-labelled.json. Its labels were PROPOSED by the agent and are not yet the
16
+ * PM's, so every agreement figure here is provisional, and the report says so.
17
+ */
18
+ 'use strict';
19
+ const fs = require('fs');
20
+ const path = require('path');
21
+ const { execFileSync } = require('child_process');
22
+ const { categorize, namedFromRecords, CATEGORIES, UNDETERMINED } = require('./lib/doc-category');
23
+ const { frontmatter } = require('./lib/related-docs');
24
+
25
+ const REPO = path.join(__dirname, '..');
26
+ const LABELS = path.join(__dirname, 'fixtures', 'doc-category-labelled.json');
27
+
28
+ function trackedMd(root) {
29
+ return execFileSync('git', ['ls-files', '*.md'], { cwd: root, encoding: 'utf8', maxBuffer: 1 << 26 }).split('\n').filter(Boolean);
30
+ }
31
+ function namedMap(root, files) {
32
+ const records = [];
33
+ for (const f of files) {
34
+ if (!/(^|\/)initiatives\//.test(f)) continue;
35
+ try { records.push({ rel: f, fm: frontmatter(fs.readFileSync(path.join(root, f), 'utf8')) || {} }); } catch { /* unreadable */ }
36
+ }
37
+ return namedFromRecords(records);
38
+ }
39
+ function one(root, rel, named) {
40
+ let text = ''; try { text = fs.readFileSync(path.join(root, rel), 'utf8'); } catch { return null; }
41
+ let fm = {}; try { fm = frontmatter(text) || {}; } catch { /* no frontmatter */ }
42
+ return { fm, c: categorize({ rel, fm, text, named }) };
43
+ }
44
+
45
+ function run(root = REPO) {
46
+ const files = trackedMd(root);
47
+ const named = namedMap(root, files);
48
+ const labelled = JSON.parse(fs.readFileSync(LABELS, 'utf8'));
49
+ const rows = []; const confusion = {};
50
+ let present = 0; let decided = 0; let right = 0;
51
+ for (const r of labelled.rows) {
52
+ const x = one(root, r.path, named);
53
+ if (!x) { rows.push({ path: r.path, expected: r.expected, got: null, missing: true }); continue; }
54
+ present++;
55
+ const got = x.c.category;
56
+ if (x.c.decided) { decided++; if (got === r.expected) right++; }
57
+ const key = r.expected + ' → ' + got;
58
+ confusion[key] = (confusion[key] || 0) + 1;
59
+ rows.push({ path: r.path, expected: r.expected, got, basis: x.c.basis, decided: x.c.decided, ok: got === r.expected });
60
+ }
61
+ const byCategory = {}; const refs = { total: 0, decided: 0, undetermined: 0 }; let undetermined = 0;
62
+ for (const f of files) {
63
+ const x = one(root, f, named); if (!x) continue;
64
+ byCategory[x.c.category] = (byCategory[x.c.category] || 0) + 1;
65
+ if (x.c.category === UNDETERMINED) undetermined++;
66
+ if (x.fm.type === 'Reference') { refs.total++; if (x.c.decided) refs.decided++; else refs.undetermined++; }
67
+ }
68
+ const untested = CATEGORIES.filter((c) => !labelled.rows.some((r) => r.expected === c));
69
+ return {
70
+ provisional: true, labelled_by: labelled.labelled_by, tenant_files: labelled.tenant_files,
71
+ files: files.length, labelled: labelled.rows.length, present, decided,
72
+ agreement_on_decided: decided ? right / decided : null, agreement_overall: present ? right / present : null,
73
+ by_category: byCategory, undetermined, reference: refs, untested_categories: untested, confusion, rows,
74
+ };
75
+ }
76
+
77
+ if (require.main === module && !process.argv.includes('--jev')) {
78
+ const r = run();
79
+ if (process.argv.includes('--json')) { console.log(JSON.stringify(r, null, 1)); process.exit(0); }
80
+ const pct = (n) => (n == null ? 'n/a' : (100 * n).toFixed(1) + '%');
81
+ console.log('doc-category-bench — PROVISIONAL: the labels were proposed by the agent, not confirmed by the PM.');
82
+ console.log(`files ${r.files} · labelled ${r.labelled} (present ${r.present}) · decided ${r.decided}`);
83
+ console.log(`agreement on decided ${pct(r.agreement_on_decided)} · overall (undetermined counts as wrong) ${pct(r.agreement_overall)}`);
84
+ console.log('by category:', JSON.stringify(r.by_category));
85
+ console.log(`Reference-typed: ${r.reference.total} · sharpened ${r.reference.decided} · left undetermined ${r.reference.undetermined}`);
86
+ console.log('untested categories (no label in the set):', r.untested_categories.join(', ') || 'none');
87
+ console.log('tenant files:', r.tenant_files);
88
+ console.log('disagreements and undetermined rows:');
89
+ for (const row of r.rows.filter((x) => !x.ok)) console.log(` ${row.path}: expected ${row.expected}, got ${row.got}${row.basis ? ' (' + row.basis + ')' : ''}`);
90
+ }
91
+ /* ── Run 3: the measured pass through Jev ─────────────────────────────────────────────────────────────────────────────── */
92
+ /* The labelled rows to measure. A row the PM corrected is marked, and the bridge never sends it. */
93
+ function labelledRows(root, corrected = {}) {
94
+ const labelled = JSON.parse(fs.readFileSync(LABELS, 'utf8'));
95
+ const rows = [];
96
+ for (const r of labelled.rows) {
97
+ let text = ''; try { text = fs.readFileSync(path.join(root, r.path), 'utf8'); } catch { continue; }
98
+ let fm = {}; try { fm = frontmatter(text) || {}; } catch { /* no frontmatter */ }
99
+ rows.push({ rel: r.path, fm, text, expected: r.expected, corrected: !!corrected[r.path] });
100
+ }
101
+ return rows;
102
+ }
103
+ /* What the labelled set can and cannot measure: a category with no label in the set is untested, whatever Jev answers. */
104
+ function labelledSet() {
105
+ const labelled = JSON.parse(fs.readFileSync(LABELS, 'utf8'));
106
+ return { labelled_by: labelled.labelled_by, tenant_files: labelled.tenant_files,
107
+ untested_categories: CATEGORIES.filter((c) => !labelled.rows.some((r) => r.expected === c)) };
108
+ }
109
+ /**
110
+ * The measured pass. `confirm` is `{ requests, fingerprint }` from the plan the PM saw: both must match the plan now, so the
111
+ * PM confirms these files and not merely this count. The key is the saved key, or TYPESAFE_API_KEY in `env`.
112
+ */
113
+ async function jevPass({ root = REPO, stateDir, confirm = null, ask, askOpts = {}, env = process.env } = {}) {
114
+ const settings = require('./lib/typesafe-settings');
115
+ const jc = require('./lib/jev-categorize');
116
+ const corrections = require('./lib/doc-category-corrections');
117
+ const sd = stateDir || require('./lib/state-root').defaultRoot();
118
+ // the workspace keys a repository by the root it registered; a symlinked checkout may be registered by either path
119
+ let real = root; try { real = fs.realpathSync(root); } catch { /* keep as given */ }
120
+ const repoRoot = [root, real].find((r) => { const f = settings.getRepo(sd, r); return f.consent || f.categorize; }) || root;
121
+ const flags = settings.getRepo(sd, repoRoot);
122
+ const cacheDir = corrections.repoDir(sd, repoRoot);
123
+ const rows = labelledRows(root, corrections.get(cacheDir));
124
+ const plan = jc.measurePlan({ rows, exclude: flags.exclude });
125
+ const set = labelledSet();
126
+ if (confirm === null) return { plan, set };
127
+ if (!confirm || !Number.isInteger(confirm.requests) || typeof confirm.fingerprint !== 'string' || !confirm.fingerprint) {
128
+ return { plan, set, out: { ok: false, error: 'Confirm with the count and the files you saw, as the plan prints them: --confirm ' + plan.requests + ':' + plan.fingerprint } };
129
+ }
130
+ const out = await jc.measure({ rows, cacheDir, key: settings.getKey(sd, env), flags, exclude: flags.exclude,
131
+ expectRequests: confirm.requests, expectFingerprint: confirm.fingerprint, ...(ask ? { ask } : {}), askOpts });
132
+ return { plan, set, out };
133
+ }
134
+
135
+ if (require.main === module && process.argv.includes('--jev')) {
136
+ const i = process.argv.indexOf('--confirm');
137
+ let confirm = null;
138
+ if (i > 0) { const m = /^(\d+):([0-9a-f]{40})$/.exec(String(process.argv[i + 1] || '')); confirm = m ? { requests: Number(m[1]), fingerprint: m[2] } : {}; }
139
+ const setLines = (set) => {
140
+ console.log('untested categories (no label in the set): ' + (set.untested_categories.join(', ') || 'none'));
141
+ console.log('tenant files: ' + (set.tenant_files || 'none'));
142
+ if (set.untested_categories.length || typeof set.tenant_files !== 'number' || !set.tenant_files) console.log('This set cannot show whether Jev separates the untested categories. Add labelled tenant files for them before reading the result as an answer.');
143
+ };
144
+ jevPass({ confirm }).then(({ plan, set, out }) => {
145
+ if (!out) {
146
+ console.log(`The measured pass would send ${plan.requests} snippet(s) of the ${plan.labelled} labelled files, about ${plan.characters} characters, to ${plan.host}. Nothing was sent.`);
147
+ console.log('It sends labelled files the local rules already decide too, because it measures Jev on them (D82 point 11).');
148
+ console.log(`${plan.withheld_count} withheld by the credential scan · ${plan.excluded} excluded · ${plan.corrected} corrected by you, not sent.`);
149
+ setLines(set);
150
+ console.log(`To send, run: node doc-category-bench.js --jev --confirm ${plan.requests}:${plan.fingerprint}`);
151
+ return;
152
+ }
153
+ if (!out.ok) { console.log('✗ ' + out.error); process.exitCode = 1; return; }
154
+ const pct = (n) => (n == null ? 'n/a' : (100 * n).toFixed(1) + '%');
155
+ console.log('Measured pass — PROVISIONAL: the labels were proposed by the agent, not confirmed by the PM. Advisory (D40).');
156
+ console.log(`sent ${out.sent} · failed ${out.failed} · answered ${out.answered} of ${out.labelled} · agreement ${pct(out.agreement)} · unsure ${out.unsure} · unreadable ${out.unreadable} · not answered ${out.not_answered}`);
157
+ console.log(`withheld by the credential scan ${out.withheld_count} · excluded ${out.excluded} · corrected by you, not sent ${out.corrected}`);
158
+ setLines(set);
159
+ for (const [c, r] of Object.entries(out.by_category)) console.log(` ${c}: ${r.right}/${r.labelled} right, ${r.unsure} unsure`);
160
+ if (out.paused) console.log('paused: ' + out.paused.reason);
161
+ console.log('disagreements:');
162
+ for (const d of out.disagreements) console.log(` ${d.doc}: expected ${d.expected}, Jev ${d.got}${d.leading ? ' (between ' + d.leading.join(' and ') + ')' : ''}`);
163
+ });
164
+ }
165
+ module.exports = { run, trackedMd, namedMap, labelledRows, labelledSet, jevPass };
@@ -0,0 +1,167 @@
1
+ {
2
+ "labelled_by": "PROPOSED by Claude from reading each file, 2026-10-04. NOT yet confirmed by the PM. Agreement against these labels is provisional until the PM confirms or corrects each row.",
3
+ "from": "this repository at the Run 1 branch point",
4
+ "tenant_files": "none yet: the PM has not supplied about 20 real meeting notes, reports and tickets, so meeting-notes and ticket are untested against real files",
5
+ "rows": [
6
+ {
7
+ "path": "planning/prd/jev-claim-checks-prd.md",
8
+ "expected": "prd"
9
+ },
10
+ {
11
+ "path": "planning/prd/workspace-cockpit-nav-prd.md",
12
+ "expected": "prd"
13
+ },
14
+ {
15
+ "path": "planning/prd/workspace-skill-authoring-prd.md",
16
+ "expected": "prd"
17
+ },
18
+ {
19
+ "path": "planning/evals/jev-claim-checks-eval-rubric.md",
20
+ "expected": "spec-artifact"
21
+ },
22
+ {
23
+ "path": "planning/evals/knowledge-graph-eval-rubric.md",
24
+ "expected": "spec-artifact"
25
+ },
26
+ {
27
+ "path": "planning/initiatives/jev-claim-checks.md",
28
+ "expected": "spec-artifact"
29
+ },
30
+ {
31
+ "path": "planning/initiatives/workspace-cockpit-nav.md",
32
+ "expected": "spec-artifact"
33
+ },
34
+ {
35
+ "path": "okf/products/pmos/adr/d80-jev-claim-pairs.md",
36
+ "expected": "spec-artifact"
37
+ },
38
+ {
39
+ "path": "okf/products/pmos/adr/d65-two-graph-adlc.md",
40
+ "expected": "spec-artifact"
41
+ },
42
+ {
43
+ "path": "okf/products/pmos/adr/d01-control-plane.md",
44
+ "expected": "spec-artifact"
45
+ },
46
+ {
47
+ "path": "DECISIONS.md",
48
+ "expected": "spec-artifact"
49
+ },
50
+ {
51
+ "path": "pm-decisions.md",
52
+ "expected": "spec-artifact"
53
+ },
54
+ {
55
+ "path": "planning/okrs/health-budgets.md",
56
+ "expected": "spec-artifact"
57
+ },
58
+ {
59
+ "path": "planning/decisions-proposed-pmos-cockpit-mod.md",
60
+ "expected": "spec-artifact"
61
+ },
62
+ {
63
+ "path": "planning/briefs/roadmap-time-model-spike-brief.md",
64
+ "expected": "spec-artifact"
65
+ },
66
+ {
67
+ "path": "planning/design-system-plan.md",
68
+ "expected": "spec-artifact"
69
+ },
70
+ {
71
+ "path": "planning/explainers/jev-claim-checks-surface-explainer.md",
72
+ "expected": "knowledge"
73
+ },
74
+ {
75
+ "path": "planning/explainers/knowledge-graph-surface-explainer.md",
76
+ "expected": "knowledge"
77
+ },
78
+ {
79
+ "path": "okf/core/concepts/frontmatter-format.md",
80
+ "expected": "knowledge"
81
+ },
82
+ {
83
+ "path": "okf/core/playbooks/working-in-pmos.md",
84
+ "expected": "knowledge"
85
+ },
86
+ {
87
+ "path": "workspace/guide/validate-with-typesafe.md",
88
+ "expected": "knowledge"
89
+ },
90
+ {
91
+ "path": "README.md",
92
+ "expected": "knowledge"
93
+ },
94
+ {
95
+ "path": "workspace/README.md",
96
+ "expected": "knowledge"
97
+ },
98
+ {
99
+ "path": "kit/mods/pmos-cockpit/README.md",
100
+ "expected": "knowledge"
101
+ },
102
+ {
103
+ "path": "CLAUDE.md",
104
+ "expected": "knowledge"
105
+ },
106
+ {
107
+ "path": "kit/AGENTS.md",
108
+ "expected": "knowledge"
109
+ },
110
+ {
111
+ "path": "kit/SECURITY.md",
112
+ "expected": "knowledge"
113
+ },
114
+ {
115
+ "path": "okf/products/pmos/references/metrics/north-star-metric.md",
116
+ "expected": "knowledge"
117
+ },
118
+ {
119
+ "path": "planning/research/agent-memory.md",
120
+ "expected": "knowledge"
121
+ },
122
+ {
123
+ "path": "discoveries.md",
124
+ "expected": "report"
125
+ },
126
+ {
127
+ "path": "planning/autonomy-log.md",
128
+ "expected": "report"
129
+ },
130
+ {
131
+ "path": "planning/evals/acceptance-gate-anti-atrophy-dogfood.md",
132
+ "expected": "report"
133
+ },
134
+ {
135
+ "path": "planning/evals/builder-profiles-dogfood.md",
136
+ "expected": "report"
137
+ },
138
+ {
139
+ "path": "planning/evals/calibration-golden-set.md",
140
+ "expected": "report"
141
+ },
142
+ {
143
+ "path": "planning/radar/jtbd-map.md",
144
+ "expected": "report"
145
+ },
146
+ {
147
+ "path": "okf/core/templates/prd-template.md",
148
+ "expected": "other"
149
+ },
150
+ {
151
+ "path": "planning/archive/phase-build-sequences.md",
152
+ "expected": "other"
153
+ },
154
+ {
155
+ "path": "planning/evals/fixtures/injection-payloads/01-html-comment.md",
156
+ "expected": "other"
157
+ },
158
+ {
159
+ "path": "okf/index.md",
160
+ "expected": "other"
161
+ },
162
+ {
163
+ "path": "okf/log.md",
164
+ "expected": "other"
165
+ }
166
+ ]
167
+ }
@@ -1,7 +1,8 @@
1
1
  ---
2
2
  type: Guide
3
3
  title: "Guide: initiatives and next steps"
4
- timestamp: 2026-10-03
4
+ description: "What the initiative detail shows, top to bottom: status, lifecycle control, and the next step the workspace proposes."
5
+ timestamp: 2026-10-04
5
6
  ---
6
7
 
7
8
  # Guide: initiatives and next steps
@@ -14,7 +15,20 @@ Select an initiative in the list. The detail opens in the detail column.
14
15
  2. **Lifecycle control.** Hold, close, cancel or reopen. Beneath it, the history of every change.
15
16
  3. **Next steps to done.** Everything between now and acceptance, in order. See below.
16
17
  4. **Phases and links.** The workflow phases and the external links.
17
- 5. **Artifacts.** Documents, conversations and files.
18
+ 5. **What the documents say.** Up to three lines, and Jev's suggestions if you turned them on. See below.
19
+ 6. **Artifacts.** Documents, conversations and files.
20
+
21
+ ## What the documents say
22
+
23
+ These lines show facts about the documents that the phases do not show. Each line gives its source: a commit, a file, a requirement id or the text of the record. A line never changes a phase, a status or a count, and nothing is written.
24
+
25
+ - **Since approval.** The PRD, rubric or brief changed in a commit after you approved the contract or accepted the work. The line gives the number of commits and the latest commit. A small edit, such as a whitespace fix, also shows here. Open the commit to see what changed.
26
+ - **Coverage.** A requirement id in the PRD that the rubric does not mention, or an id in the rubric that the PRD does not have. A mention is not a grade. If the rubric mentions no requirement id, the line says that coverage cannot be found.
27
+ - **Record says.** The `status:` line in the initiative record names a stage that the phases contradict. Edit the record if the line is wrong. The phases do not change.
28
+
29
+ If a check cannot run, for example in a shallow clone, the line says "Cannot tell" and gives the reason. A missing line means that the check has nothing to compare.
30
+
31
+ If **read documents for status** is on for the repository, lines that start with **Documents suggest** can follow. They are Jev's answers about the documents of this initiative: a PRD, rubric or brief that may be a stub, or a note that may contradict a requirement. Each line says *very likely*, *likely* or *possible*, says that it is a suggestion and names the model. Open the document to decide. See [Guide: validate a repository](validate-with-typesafe.md).
18
32
 
19
33
  ## Next steps
20
34
 
@@ -1,7 +1,8 @@
1
1
  ---
2
2
  type: Guide
3
3
  title: "Guide: find knowledge without reading whole files"
4
- timestamp: 2026-10-03
4
+ description: "How to use pmos kg to find one card, section or decision without reading whole files, and the limits of the map."
5
+ timestamp: 2026-10-04
5
6
  ---
6
7
 
7
8
  # Guide: find knowledge without reading whole files
@@ -18,7 +19,7 @@ The map has an **edge** only where a file states a relation:
18
19
  |---|---|
19
20
  | link | A Markdown link to a tracked file. |
20
21
  | frontmatter | A path in `prd_path`, `rubric_path`, `explainer_path` or `sources`. |
21
- | supersedes, amended_by, cites | A document that chooses to carry one of these keys. |
22
+ | supersedes, amended_by, cites, initiative | A document that chooses to carry one of these keys. |
22
23
  | initiative | An `initiative_id` that names an initiative record. |
23
24
  | anchor | A `parent_kr` of `health:<id>` or a KR id. |
24
25
  | cites (mention) | A `D<nn>` in the text, such as `D65`, with two or more digits. |
@@ -27,6 +28,10 @@ A link that points at a missing file is **dangling**. It is listed and never bec
27
28
 
28
29
  It does not guess. A file that talks about a topic without a link, a path or a `D<nn>` is not connected to it. It does not use embeddings or a model, and it does not use the network.
29
30
 
31
+ ## Typed keys
32
+
33
+ A document may carry `supersedes`, `amended_by`, `cites` and `initiative` in its frontmatter. Each key is one line and holds one or more paths to `.md` files, separated by commas. The map reads each path as a stated edge of that type. A path to a file that does not exist is listed as dangling and never becomes an edge. Nobody fills these keys in for existing documents; add them to new or edited ones. `pmos kg stats` shows how many edges are typed and how many are inferred from a `D<nn>` in the text.
34
+
30
35
  ## Commands
31
36
 
32
37
  | Command | What it does |
@@ -49,6 +54,88 @@ An unknown file, heading or decision stops with a message that says what was not
49
54
  - A file with no incoming link is not an error. It means no other file links to it.
50
55
  - A **derived** card comes from the first paragraph of a file that has no `description`.
51
56
 
57
+ ## Categories
58
+
59
+ Each Markdown file also has a **category**. The category is the kind of document as you would name it: `prd`, `meeting-notes`, `report`, `ticket`, `knowledge`, `spec-artifact` or `other`. A ticket also has a kind: `design`, `engineering` or `other`. The category is kept beside the `type` in the frontmatter. It does not replace the `type`, and it does not change a file.
60
+
61
+ The workspace decides the category with local rules only. It uses no model and sends nothing. It uses the first rule that applies, in this order:
62
+
63
+ 1. A `category:` line the author wrote.
64
+ 2. A `type` that maps to a category.
65
+ 3. An initiative record that names the file.
66
+ 4. The file name or the folder.
67
+ 5. The headings.
68
+
69
+ When a later rule disagrees with the rule that decided, the file keeps the earlier answer. A `Reference` or `Research` file with no better evidence has no category. It shows as `undetermined`. That is not the same as `other`: `other` means the file is none of the kinds, and `undetermined` means no rule could tell.
70
+
71
+ The Knowledge view shows the category after each document name. Point at it to see which rule decided. Press a category above the list to show only those documents, and press it again to show all. The category is advisory. `pmos kg stats` counts the files in each category.
72
+
73
+ ### Ask Jev about undetermined files
74
+
75
+ Jev can give a category to a file that the local rules leave `undetermined`. This is off until you turn it on. Jev never sees a file that already has a category from a local rule or from your correction. The measured pass below is the one exception for the local rules.
76
+
77
+ Three things must all be on before anything is sent:
78
+
79
+ 1. A TypeSafe key, saved on the Validate page.
80
+ 2. Validation, switched on for this repository.
81
+ 3. **Categorize with Jev**, the switch in the Knowledge view.
82
+
83
+ If one of them is off, the Knowledge view tells you which one.
84
+
85
+ To start:
86
+
87
+ 1. Turn on **categorize with Jev**.
88
+ 2. Press **ask Jev about undetermined files**. The view shows how many requests it will send, about how many characters, and the host. Nothing is sent yet.
89
+ 3. Press **confirm and send** to send that number. If the files changed after you looked, even when the number is the same, the send stops and you look again.
90
+
91
+ After the first confirmed send, the workspace sends only new or changed undetermined files. It does this when it reads the documents of this repository, at most once a minute. It reads them when it starts and when you open the Knowledge view. If more than 25 files wait, it does not send them. It tells you, and you confirm the count again.
92
+
93
+ What is sent for each file: the title, the headings and the start of the text, at most 3,000 characters. The file path and the whole file are not sent. A file under an excluded path is not sent. A file whose start looks like a password or a key is not sent.
94
+
95
+ Jev's answer shows as the category with `(jev)` after it. Point at it to see how sure Jev was, in words. If Jev was not sure, the file stays `undetermined`, and the tooltip names the two categories Jev was between. A Jev category is advisory. Each answer is kept on your computer, outside the repository, and the same text is not sent twice. Nothing is written into the repository.
96
+
97
+ If the service rejects the key two times, cannot be reached three times in a row, or sends three answers in a row that the workspace cannot read, sending stops for this repository. An answer that cannot be read is not sent again. Press **resume** to start again.
98
+
99
+ ### Measure how well Jev categorizes
100
+
101
+ The measured pass sends the files of the labelled set to Jev and compares the answers with the labels. It runs only when you start it in a terminal. The workspace never starts it.
102
+
103
+ The measured pass also sends labelled files that a local rule already decides, because it measures Jev on them. It does not send a file that you corrected. The three switches, the excluded paths and the password and key check apply as for the Knowledge view.
104
+
105
+ 1. Run `node workspace/doc-category-bench.js --jev`. It shows how many files it would send, about how many characters, the host, and a `--confirm` value. It sends nothing.
106
+ 2. Read which categories the labelled set cannot test. When a category has no label in the set, the result says nothing about that category.
107
+ 3. To send, run the command again with `--confirm` and the value it showed, for example `--confirm 40:372a…`. The value holds the count and the files. If the files changed, nothing is sent and you look again.
108
+
109
+ It uses the key that you saved on the Validate page, or `TYPESAFE_API_KEY` in the terminal. It prints the agreement, each category, how many answers were unsure, how many files were not sent and why, and every disagreement. The labels in the set are not yet confirmed, so the result is provisional. Only one pass at a time runs for a repository. If the workspace is sending, the command stops and sends nothing.
110
+
111
+ ### Correct a category
112
+
113
+ When a category is wrong, set the correct one. Your correction comes before all five rules.
114
+
115
+ 1. In the Knowledge view, press **fix** at the end of the document row.
116
+ 2. Select the category. For a `ticket`, also select the ticket kind.
117
+ 3. Press **save correction**.
118
+
119
+ The category after the document name changes. Point at it: it says `decided by pm correction`. When a rule gives a different category, the text names that rule too.
120
+
121
+ The workspace keeps your corrections in its runtime state directory, never in the repository. Each repository has its own corrections. A correction does not change a file. To remove a correction, press **fix** and then **clear correction**. The rules then decide again.
122
+
123
+ ### Write the category into the file
124
+
125
+ You can also write the category into the document, as a `category:` line in its frontmatter. This is a separate step, and it is the only step here that changes a file.
126
+
127
+ 1. Press **fix** and select the category.
128
+ 2. Press **write it into the file**. The workspace shows the change. It does not write yet.
129
+ 3. Examine the change. Press **apply** to write it, or **cancel**.
130
+
131
+ The workspace changes one `category:` line in the frontmatter block at the top of the file. For a ticket, it also changes one `ticket_kind:` line. It adds the line when there is none. No other line of the file changes.
132
+
133
+ The workspace does not write when:
134
+
135
+ - The file has no frontmatter block at the top. It does not add a block.
136
+ - The frontmatter has two `category:` lines.
137
+ - The file changed after the workspace showed you the change. Press **write it into the file** again to see the new change.
138
+
52
139
  ## Where the map is kept
53
140
 
54
141
  The command reads the tracked `.md` files of the Git repository you run it in. It keeps the map in the runtime state directory of the workspace, never in the repository. It builds the map again when the Git HEAD changes or when you edit a tracked `.md` file that is not yet committed. It does not use the cache for any other change.
@@ -1,12 +1,39 @@
1
1
  ---
2
2
  type: Guide
3
3
  title: "Guide: layout"
4
- timestamp: 2026-10-03
4
+ description: "The three workspace columns: navigation, detail and tabs, and what each one holds."
5
+ timestamp: 2026-10-04
5
6
  ---
6
7
 
7
8
  # Guide: layout
8
9
 
9
- The workspace has three columns. They are, from left to right, navigation, detail and tabs.
10
+ On a desktop screen the workspace has four columns. They are, from left to right, the repository rail, the list, the detail and the tabs. A phone, and the address option `?layout=legacy`, keep the older layout with three columns: navigation, detail and tabs. The rest of this page describes those three columns.
11
+
12
+ ## The repository rail, the list and the tools
13
+
14
+ The rail is 64 pixels wide and shows one tile for each repository. A dot on a tile means that repository has something that needs you. The selected repository has no dot, because the list beside it shows what needs you. The `+` tile adds a repository and the menu button opens the tools: knowledge, memory, skills, scheduled tasks, validate, the guide and the others.
15
+
16
+ A tool opens as a tab, not in place of the list. Choose the same tool again and the existing tab comes to the front.
17
+
18
+ Press ⌘K (Ctrl-K) to jump to anything: an initiative, a repository, an open tab, a session or a tool. Type to filter, use the arrow keys to choose, and press Enter. Esc closes it.
19
+
20
+ Press ⌘↵ (Ctrl-Enter) to enter Focus. The rail and the list go out of view, the active tab takes the width, and the detail stays as a narrow slice on the right. Press Esc or Navigate to return. Focus does not restart a terminal.
21
+
22
+ Today is a tool tab. It shows what needs you, with an action and a reason to dismiss each item, what is being prepared, and a Repository / All switch.
23
+
24
+ Each tool tab shows a board: Today, All repos, Knowledge, Memory, Skills, Scheduled tasks, Validate, the guide and Reports. Reports is in the menu, under Library, and lists only the run reports that exist. If a part of a board has no data behind it, the board does not show that part, or it says that the data is not read.
25
+
26
+ The `+` tile on the rail opens Add repository as a tab. The tab shows the scan of the folder before you add it.
27
+
28
+ The list has a switch for Initiatives and Files. Choose Files to see the files of the current repository. Type in the filter to find a file. Press a file to open it in a tab.
29
+
30
+ Under a terminal tab, the scratchpad holds a note that you can send to the terminal, copy or clear. The `+` session button opens a chooser for the new session. In a document tab, the Alignment button opens a drawer on the right. It shows the consistency findings for the document and lists only the documents that have a concrete tie to it.
31
+
32
+ The list has a switch for Work, Today and All repos, a filter, and the groups Needs you, In progress and Done. The badge on a row names the step where the initiative is, for example `review`, `accept` or `build`.
33
+
34
+ The detail shows the title, the summary, the chips, the blocker (when the initiative is blocked), the phases and the artifacts. Only the phase where the initiative is opens by itself, and the task where it is has a highlight. Press a phase to open or close it. Under each artifact are its link and sync controls.
35
+
36
+ Each tab shows a dot, its title and, under the title, its initiative and its session. Press × on a tab to close it. The `+ session` button is at the right end of the tabs.
10
37
 
11
38
  ## Navigation
12
39
 
@@ -31,6 +58,7 @@ Press a file anywhere in the workspace to open it in a tab. Any text file opens,
31
58
  - **Markdown** opens as a document. You can edit it and save it.
32
59
  - **JSON** opens pretty-printed. If the file is not valid JSON, the tab shows the text as written and says why.
33
60
  - **JSONL** opens as numbered records, one for each line. The tab marks a line that is not valid JSON.
61
+ - **HTML** opens in `view` as the page it is. Use `raw` to see its source. The page cannot run scripts and cannot load anything from the network. To run the page's own scripts, press `run scripts`. This grant is for this tab only. The page can then run the scripts written inside it, but it still cannot load anything from the network, cannot load a script from another location, and cannot read the workspace. Press `scripts running — stop` to stop the scripts. Closing the tab also removes the grant.
34
62
  - **Other text** opens as numbered source lines. Spreadsheets, PDFs and images open in their own views.
35
63
 
36
64
  A tab for a file that is not markdown is read-only. It has no edit, diff or save control, because the workspace saves only markdown.
@@ -1,7 +1,8 @@
1
1
  ---
2
2
  type: Guide
3
3
  title: "Guide: Today and the proactive engine"
4
- timestamp: 2026-10-03
4
+ description: "What the Today item shows, how the morning brief is made, and how the workspace works out what is waiting for you."
5
+ timestamp: 2026-10-04
5
6
  ---
6
7
 
7
8
  # Guide: Today and the proactive engine
@@ -39,6 +40,22 @@ From a Today item that names an initiative, you can ask for the plan and approve
39
40
 
40
41
  The states of an entry are queued, started, skipped, expired and cancelled.
41
42
 
43
+ ## What it learns
44
+
45
+ Today learns from you, and only from you. The evidence is your own records on this computer: the verdict you give an item, what you do with it (opened, acted, dismissed, ignored), and your accept or send-back on a prepared session. The workspace makes no model call to do this. The weights are arithmetic, and the lessons are written from fixed templates filled with counts.
46
+
47
+ - **Weights.** Three adjustments are added to an item's place in the order: one for its trigger (at most 8 points), one for its lane (at most 4) and one for its trigger at this time of day (at most 3). Time of day is morning 05-12, afternoon 12-17, evening 17-22 and night, in your local time. A cell moves only after 5 events, and moves less than the raw average. The total is at most 15, so learning reorders items that are close and cannot turn the stated order upside down. An item shows its adjustment and the counts behind it.
48
+ - **What it never does.** It never lowers or hides what blocks on you: the needs and returned triggers get no negative adjustment and are never narrowed. Items of the morning brief teach no weight, because their order is fixed. Pause learning makes every weight zero and stops the weekly review, the proposals and the narrowing; feedback is still recorded.
49
+ - **The weekly review.** About every 7 days, only while the workspace server runs, the workspace takes one snapshot. It does not catch up after a gap: it takes one snapshot of the last 7 days. **run the weekly review now** does the same on demand and is refused while learning is paused. Quiet hours hold scans, not this review. The review runs from the proactive tick, so while the engine is switched off there is no weekly review either; **run the weekly review now** still works.
50
+ - **The rollup.** For the last 7 days: the hit rate (useful and acted, over useful, acted, not useful, wrong priority and dismissed), the used-as-is rate (accepted over accepted and sent back), corrections per prepared run (sends-back over every session started from a step, reviewed or not), and the items you reported as missed. Every rate shows its count. With fewer than 5 rated items, or 3 runs, it says there is not enough evidence and shows no rate.
51
+ - **The watermelon check.** It looks only when the hit rate is 0.6 or more. It then names any of these: 2 or more items reported as missed, a used-as-is rate under 0.5 over 3 or more reviewed runs, or 0.5 or more corrections per prepared run over 3 or more runs. A good hit rate with one of these under it is not a good week.
52
+ - **Lessons.** The review proposes lessons from four templates: a trigger that was useful under 40% or at least 80% of the time (5 or more ratings), items you reported as missed (2 or more), and a trigger that did badly in one part of the day only. A lesson is a proposal. It is approved, rejected or retired only when you press the button. A rejected lesson is not proposed again for 28 days. At most 10 lessons are approved at once.
53
+ - **Where an approved lesson goes.** Into the state folder, and from there to the morning-brief session, which reads it with `pmos proactive learn lessons`. It goes into no other prompt, so the prompt and the plan hash of a prepared step do not change, and no file of any repository is written. To keep a lesson in repository memory, copy it there yourself.
54
+ - **Narrowing.** A trigger other than needs and returned that you rated useful under 30% of the time over 10 or more ratings in 28 days is left off Today. Today says so, with the numbers and a **restore** button. The trigger is still scanned. A restore holds until 10 new ratings of that trigger have arrived.
55
+ - **forget what it learned** makes the workspace ignore all earlier feedback from then on. Nothing is deleted.
56
+
57
+ The records are `learn-events.jsonl` (restore and reset), `lessons.jsonl` and `proactive-rollups.jsonl`, in the workspace state folder. They are not committed.
58
+
42
59
  ## What it shows
43
60
 
44
61
  Today scans for five conditions. Each one becomes one item with the reason and the evidence.
@@ -60,7 +77,7 @@ The order is fixed and you can read it: what blocks on you first, then an unrevi
60
77
  - dismiss 7 days hides the item for 7 days.
61
78
  - something was missed records a note about what Today should have shown. It needs a note.
62
79
 
63
- A verdict never changes a status, a lifecycle record or an acceptance. In this release a verdict is only recorded. Nothing uses it to change the order yet.
80
+ A verdict never changes a status, a lifecycle record or an acceptance. It is recorded, and it is evidence for what Today learns, as described above.
64
81
 
65
82
  ## Settings
66
83
 
@@ -74,7 +91,11 @@ A verdict never changes a status, a lifecycle record or an acceptance. In this r
74
91
  | name | Used in the greeting. |
75
92
  | stalled after N days | The stalled threshold, from 1 to 90 days. |
76
93
  | quiet hours | A start and an end time. No automatic scan runs between them. |
77
- | pause learning | Saved for later. Nothing learns from your verdicts in this release, so it changes nothing today. |
94
+ | pause learning | Stops all learning: weights are zero, and there is no weekly review, no proposal and no narrowing. Feedback is still recorded. |
95
+
96
+ When you save a setting, Today shows the saved value immediately. A read that started before the save does not replace it.
97
+
98
+ Text that you type in a setting (the name, the stalled days, the quiet hours) or in the "something was missed" box stays in the box until you save it. A refresh of Today does not clear it, and the box keeps the focus.
78
99
 
79
100
  ## The commands
80
101
 
@@ -83,12 +104,13 @@ A verdict never changes a status, a lifecycle record or an acceptance. In this r
83
104
  - `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`, `--brief-scoring on|off` or `--name` to change one.
84
105
  - `pmos proactive brief` shows today's brief. `pmos proactive brief submit --file PATH` submits one. `pmos proactive brief schedule` makes the 06:45 schedule for the current repository.
85
106
  - `pmos proactive prepare` shows the queue. `pmos proactive prepare plan --initiative ID` shows a plan and its hash. `pmos proactive prepare queue --initiative ID --item ITEM --sha HASH` approves it. `pmos proactive prepare cancel ID` cancels an entry. `pmos proactive prepare run` looks at the queue now.
107
+ - `pmos proactive learn` shows the rollup, the weights, the narrowed triggers and the lessons. `pmos proactive learn run` takes the weekly review now. `pmos proactive learn lessons` lists the approved lessons. `pmos proactive learn approve ID`, `reject ID` and `retire ID` decide a lesson. `pmos proactive learn restore TRIGGER` brings back a narrowed trigger. `pmos proactive learn reset` makes it ignore earlier feedback.
86
108
  - `pmos proactive feedback <id> useful|not-useful|wrong-priority|opened|dismissed` records feedback. Use `--missed "text"` for a missed item.
87
109
 
88
110
  ## Limits
89
111
 
90
112
  - It runs only while the workspace server runs. It does not catch up for time the server was off. A sleeping computer gets no scan.
91
113
  - The items from the records come from this computer. The workspace itself does not read your mail or your calendar. Only the morning-brief session does, and only the sources you switched on.
92
- - It does not start a session, push, send, post, accept or merge. It only shows items and records your feedback. The one exception is background preparation, which starts a session only for a plan you approved, as described above.
114
+ - It does not start a session, push, send, post, accept or merge. It only shows items and records your feedback, and learns from it as described above. The one exception is background preparation, which starts a session only for a plan you approved, as described above.
93
115
  - An unreadable repository is listed as not read. An empty list is not proof that nothing waits there.
94
116
  - The records stay in the workspace state folder on this computer. They are not committed.
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  type: Guide
3
3
  title: "Guide: scheduled tasks and the command line"
4
+ description: "How scheduled tasks run a skill in a terminal session, their limits, and the command line that manages them."
4
5
  timestamp: 2026-10-03
5
6
  ---
6
7
 
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  type: Guide
3
3
  title: "Guide: statuses"
4
+ description: "The nine initiative statuses, who acts on each, and why a status is worked out again each time and never typed."
4
5
  timestamp: 2026-10-03
5
6
  ---
6
7