create-pmos 0.47.0 → 0.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/package.json +1 -1
  2. package/payload/AGENTS-HUMAN.md +1 -1
  3. package/payload/AGENTS.md +1 -1
  4. package/payload/KIT-VERSION +2 -2
  5. package/payload/mods/pmos-cockpit/.claude-plugin/plugin.json +1 -1
  6. package/payload/mods/pmos-cockpit/README.md +14 -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/route.mjs +95 -0
  11. package/payload/mods/pmos-cockpit/hooks/register.tsx +104 -19
  12. package/payload/mods/pmos-cockpit/types/index.d.ts +1 -1
  13. package/payload/planning/evals/eval-calibration.md +1 -1
  14. package/payload/planning/okrs/health-budgets.md +1 -1
  15. package/payload/scripts/pmos-log.sh +18 -8
  16. package/payload/scripts/pmos-model.js +233 -2
  17. package/payload/scripts/status-facts.js +214 -0
  18. package/payload/scripts/workflow-state.js +59 -2
  19. package/payload/state/discoveries.md +1 -1
  20. package/payload/state/log.md +1 -1
  21. package/workspace/README.md +2 -2
  22. package/workspace/bin/live-notes.js +133 -0
  23. package/workspace/design-parity.js +328 -21
  24. package/workspace/doc-category-bench.js +165 -0
  25. package/workspace/fixtures/doc-category-labelled.json +167 -0
  26. package/workspace/guide/index.md +2 -1
  27. package/workspace/guide/initiatives-and-next-steps.md +17 -2
  28. package/workspace/guide/knowledge-graph.md +94 -1
  29. package/workspace/guide/layout.md +31 -2
  30. package/workspace/guide/live-notes.md +78 -0
  31. package/workspace/guide/proactive-engine.md +6 -2
  32. package/workspace/guide/validate-with-typesafe.md +31 -2
  33. package/workspace/lib/claims.js +51 -2
  34. package/workspace/lib/doc-category-corrections.js +113 -0
  35. package/workspace/lib/doc-category-writeback.js +110 -0
  36. package/workspace/lib/doc-category.js +225 -0
  37. package/workspace/lib/engine/pmos-model.js +233 -2
  38. package/workspace/lib/engine/status-facts.js +214 -0
  39. package/workspace/lib/engine/workflow-state.js +59 -2
  40. package/workspace/lib/jev-categorize.js +433 -0
  41. package/workspace/lib/jev-claims.js +59 -15
  42. package/workspace/lib/jev-live.js +37 -0
  43. package/workspace/lib/jev-route.js +139 -0
  44. package/workspace/lib/kg-cli.js +3 -1
  45. package/workspace/lib/kg.js +6 -1
  46. package/workspace/lib/learn.js +5 -3
  47. package/workspace/lib/live-notes.js +1035 -0
  48. package/workspace/lib/typesafe-settings.js +10 -2
  49. package/workspace/package-lock.json +1 -1
  50. package/workspace/package.json +5 -4
  51. package/workspace/public/pm/app.js +148 -79
  52. package/workspace/public/pm/data.js +78 -0
  53. package/workspace/public/pm/fixture.js +40 -2
  54. package/workspace/public/pm/modes-live.js +279 -18
  55. package/workspace/public/pm/panel-cockpit.js +638 -0
  56. package/workspace/public/pm/panel-detail.js +212 -8
  57. package/workspace/public/pm/panel-initiatives.js +9 -9
  58. package/workspace/public/pm/panel-newsess.js +145 -86
  59. package/workspace/public/pm/panel-repos.js +34 -17
  60. package/workspace/public/pm/panel-tabs.js +249 -29
  61. package/workspace/public/pm/state.js +48 -4
  62. package/workspace/public/pm/tab-doc.js +244 -16
  63. package/workspace/public/pm/tab-term.js +11 -1
  64. package/workspace/public/pm/tokens.css +4 -3
  65. package/workspace/public/pm/view-addrepo.js +207 -0
  66. package/workspace/public/pm/view-files.js +197 -0
  67. package/workspace/public/pm/view-guide.js +141 -0
  68. package/workspace/public/pm/view-knowledge.js +225 -0
  69. package/workspace/public/pm/view-livenotes.js +397 -0
  70. package/workspace/public/pm/view-memory.js +122 -0
  71. package/workspace/public/pm/view-portfolio.js +153 -0
  72. package/workspace/public/pm/view-reports.js +190 -0
  73. package/workspace/public/pm/view-scheduled.js +161 -0
  74. package/workspace/public/pm/view-skills.js +216 -0
  75. package/workspace/public/pm/view-today.js +168 -0
  76. package/workspace/public/pm/view-validate.js +171 -0
  77. package/workspace/server.js +349 -7
  78. package/workspace/skills/live-notes.skill +58 -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,7 @@
1
1
  ---
2
2
  type: Guide
3
3
  title: "Guide: start here"
4
- timestamp: 2026-10-03
4
+ timestamp: 2026-10-04
5
5
  ---
6
6
 
7
7
  # Guide: start here
@@ -38,4 +38,5 @@ The workspace has three columns. [Layout](layout.md) explains where things open.
38
38
  - [Scheduled tasks and the command line](scheduled-tasks-and-cli.md): how to make, edit and run scheduled tasks.
39
39
  - [Validate a repository](validate-with-typesafe.md): what each kind of document is compared with, how to read the verdict, and what leaves your machine.
40
40
  - [Today and the proactive engine](proactive-engine.md): what Today finds, what you can do with an item, the settings, the commands and the limits.
41
+ - [Live notes](live-notes.md): take notes in a meeting, confirm each talking point, and what goes to Jev when you turn it on.
41
42
  - [Find knowledge without reading whole files](knowledge-graph.md): what `pmos kg` finds, its commands, how to read its numbers, and its limits.
@@ -2,7 +2,7 @@
2
2
  type: Guide
3
3
  title: "Guide: initiatives and next steps"
4
4
  description: "What the initiative detail shows, top to bottom: status, lifecycle control, and the next step the workspace proposes."
5
- timestamp: 2026-10-03
5
+ timestamp: 2026-10-04
6
6
  ---
7
7
 
8
8
  # Guide: initiatives and next steps
@@ -15,7 +15,22 @@ Select an initiative in the list. The detail opens in the detail column.
15
15
  2. **Lifecycle control.** Hold, close, cancel or reopen. Beneath it, the history of every change.
16
16
  3. **Next steps to done.** Everything between now and acceptance, in order. See below.
17
17
  4. **Phases and links.** The workflow phases and the external links.
18
- 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. If no check has anything to compare yet, the section says "Nothing to compare yet" and tells you what each check waits for, such as an approved contract, a feature lane or a stage word in the status line.
30
+
31
+ In a documents repository (the pm-artifacts profile), only **Since approval** applies. It starts when you accept the work: after that, a commit to the brief or the deliverable shows here. Coverage and Record says are for product-code repositories.
32
+
33
+ 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).
19
34
 
20
35
  ## Next steps
21
36
 
@@ -2,7 +2,7 @@
2
2
  type: Guide
3
3
  title: "Guide: find knowledge without reading whole files"
4
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-03
5
+ timestamp: 2026-10-04
6
6
  ---
7
7
 
8
8
  # Guide: find knowledge without reading whole files
@@ -54,6 +54,99 @@ An unknown file, heading or decision stops with a message that says what was not
54
54
  - A file with no incoming link is not an error. It means no other file links to it.
55
55
  - A **derived** card comes from the first paragraph of a file that has no `description`.
56
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` with `(jev unsure)` after it, 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
+ After a pass, the Knowledge view shows what happened to each undetermined file. Files with a problem are at the top of the list:
98
+
99
+ - **failed**: the request did not succeed. The row gives the reason. The file keeps the local result, and the next pass sends it again.
100
+ - **not asked**: sending stopped before this file. The row gives the reason.
101
+ - **unreadable**: the workspace could not read Jev's answer. The file is not sent again until it changes.
102
+ - **unsure**: Jev was not sure. The row names the two categories, and the file stays `undetermined`.
103
+ - **withheld** or **excluded**: the file was not sent. The row says why.
104
+ - **categorized**: Jev gave a category. The row names it.
105
+
106
+ A line above the list counts the files in the view: decided by the local rules or your corrections, categorized by Jev, and still `undetermined`.
107
+
108
+ 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.
109
+
110
+ ### Measure how well Jev categorizes
111
+
112
+ 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.
113
+
114
+ 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.
115
+
116
+ 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.
117
+ 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.
118
+ 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.
119
+
120
+ 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.
121
+
122
+ ### Correct a category
123
+
124
+ When a category is wrong, set the correct one. Your correction comes before all five rules.
125
+
126
+ 1. In the Knowledge view, press **fix** at the end of the document row.
127
+ 2. Select the category. For a `ticket`, also select the ticket kind.
128
+ 3. Press **save correction**.
129
+
130
+ 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.
131
+
132
+ 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.
133
+
134
+ ### Write the category into the file
135
+
136
+ 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.
137
+
138
+ 1. Press **fix** and select the category.
139
+ 2. Press **write it into the file**. The workspace shows the change. It does not write yet.
140
+ 3. Examine the change. Press **apply** to write it, or **cancel**.
141
+
142
+ 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.
143
+
144
+ The workspace does not write when:
145
+
146
+ - The file has no frontmatter block at the top. It does not add a block.
147
+ - The frontmatter has two `category:` lines.
148
+ - The file changed after the workspace showed you the change. Press **write it into the file** again to see the new change.
149
+
57
150
  ## Where the map is kept
58
151
 
59
152
  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.
@@ -2,12 +2,40 @@
2
2
  type: Guide
3
3
  title: "Guide: layout"
4
4
  description: "The three workspace columns: navigation, detail and tabs, and what each one holds."
5
- timestamp: 2026-10-03
5
+ timestamp: 2026-10-04
6
6
  ---
7
7
 
8
8
  # Guide: layout
9
9
 
10
- 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.
37
+
38
+ If many tabs are open, the strip stays one row and scrolls sideways. Use the mouse wheel on the strip to scroll it. The tab in front always scrolls into view. Press `⇅ tabs at side` to show the tabs as a list down the left of the tabs column. In the list, each tab is one row, with its title, its initiative and session, and ×. The `+ session` button is at the top of the list, and the other controls are at the bottom. Press `⇆ tabs on top` to go back to the strip. The workspace remembers your choice in this browser. A phone and `?layout=legacy` always show the strip.
11
39
 
12
40
  ## Navigation
13
41
 
@@ -32,6 +60,7 @@ Press a file anywhere in the workspace to open it in a tab. Any text file opens,
32
60
  - **Markdown** opens as a document. You can edit it and save it.
33
61
  - **JSON** opens pretty-printed. If the file is not valid JSON, the tab shows the text as written and says why.
34
62
  - **JSONL** opens as numbered records, one for each line. The tab marks a line that is not valid JSON.
63
+ - **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.
35
64
  - **Other text** opens as numbered source lines. Spreadsheets, PDFs and images open in their own views.
36
65
 
37
66
  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.
@@ -0,0 +1,78 @@
1
+ ---
2
+ type: Guide
3
+ title: "Guide: live notes"
4
+ description: "How the Live notes view listens to a meeting, turns what is said into talking points, and lets you confirm each one, with an optional check by Jev."
5
+ timestamp: 2026-10-04
6
+ ---
7
+
8
+ # Guide: live notes
9
+
10
+ The Live notes view takes notes during a meeting. Each thing that is said becomes a talking point. The workspace guesses which open initiative the point is about. You confirm the guess, change it or reject it, one card at a time. Nothing changes an initiative until you confirm.
11
+
12
+ ## Open the view
13
+
14
+ 1. Select the repository in the navigation column.
15
+ 2. Open the menu and select **live notes** under **Library**. The view opens as a tab for that repository.
16
+ 3. Press **New session**. A session is one meeting.
17
+
18
+ If the repository has more than 255 open initiatives, the view asks you to select an anchor first. The session then uses only the initiatives of that anchor.
19
+
20
+ ## Add talking points
21
+
22
+ You can type the points or let the view listen.
23
+
24
+ - **Type.** Write one point for each line. Press Enter or **Add**. Shift+Enter starts a new line.
25
+ - **Listen.** Press **Start listening**. The browser recognises speech on this device. No audio leaves the device. The first time, the browser can ask you to download the language. Press **Download for** and the language name.
26
+
27
+ While the view listens, it shows **● Listening: notes are being taken**. Words that are not final show in grey and are not added. A phrase becomes a point at the end of a sentence or after a pause. Before the phrase becomes a point, the workspace cleans it: it removes filler words (um, uh, er, erm, hmm) and repeated words, adds a capital letter and adds a full stop.
28
+
29
+ Press **Stop** to stop listening. Press **This meeting is private** to stop listening and mark the session private. Nothing from a private session goes to Jev.
30
+
31
+ A browser that cannot recognise speech on the device says so. The view never sends audio to a cloud service. Chrome 139 and later can recognise speech on the device.
32
+
33
+ ## Confirm each point
34
+
35
+ Each point shows as a card. Cards that wait for you are at the top. The card shows the guess and where the guess came from:
36
+
37
+ | Source | What it means |
38
+ |---|---|
39
+ | local word match | The workspace compared the words of the point with the titles, intents and open questions of the open initiatives. A local guess is never shown above *possible*. |
40
+ | Jev | Jev compared the point with the same titles, intents and open questions. Its band is in words: *very likely*, *likely* or *possible*. |
41
+ | agent session | An agent session placed the point with the `live-notes` command. |
42
+
43
+ On each card:
44
+
45
+ 1. Select the initiative, or **none of these initiatives**.
46
+ 2. Select how the point relates to it: **validates**, **clarifies**, **extends**, **contradicts** or **answers a question**.
47
+ 3. For **answers a question**, select which open question it answers. The view refuses **answers a question** without one.
48
+ 4. Press **Confirm** or **Reject**.
49
+
50
+ If you change nothing, Confirm records *accept*. If you change the initiative, the relation or the question, it records *change*. The counter shows how many points are confirmed, changed, rejected, waiting and withheld.
51
+
52
+ A point that looks like a secret, such as a key or a token, is withheld. The view shows the reason and never the text.
53
+
54
+ ## Check with Jev
55
+
56
+ **Check with Jev** is off until you turn it on. When it is off, every guess is a local word match and nothing leaves this machine.
57
+
58
+ Jev checks a point only when all three of these are true:
59
+
60
+ 1. A TypeSafe key is saved (open **validate** in the settings and tools drawer).
61
+ 2. Validation is turned on for this repository (also under **validate**).
62
+ 3. **Check with Jev** is on in the Live notes view. It applies to this repository only.
63
+
64
+ If one is missing, the view says which one, in words.
65
+
66
+ When all three are true, the workspace sends each cleaned point to TypeSafe. It sends only the cleaned sentence and the titles, intents and open questions of the initiatives in the session. It does not send the speaker's name. It does not send a withheld point. It does not send a point from a private session. The key goes only in the request header.
67
+
68
+ If Jev cannot be reached, or its answer cannot be read, the card still gets the local guess. The card says that Jev was not reached. If the service rejects the key twice, or cannot be reached three times in a row, Jev pauses for that session and the view shows the reason. The local word match continues.
69
+
70
+ ## Close the session
71
+
72
+ Press **End and file the note**. The workspace writes a section in the day's requirements note, with your decisions. A rejected point is left out and counted. The raw transcript is deleted. The view shows the path of the note. A closed session is read-only.
73
+
74
+ ## Limits
75
+
76
+ - The local word match compares words only. It does not understand meaning.
77
+ - How well speech recognition and Jev work on a real meeting has not been measured. The share of guesses you change is the measure.
78
+ - The view does not read meetings from Wispr Flow. The `live-notes` command does that in an agent session.