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,171 @@
1
+ /* pm/view-validate.js — the Validate view, drawn from planning/design/cockpit-mockup/ViewValidate.dc.html.
2
+ *
3
+ * PRD workspace-cockpit-nav, Run 3 (R4). The board is a header with two actions on the right, a row of five stat cards, a
4
+ * table of findings and a closing note. The content is the view's existing list (modes-live.js `validateList`): this module
5
+ * draws it into a host and arranges its parts into the board. No node is removed and no text is changed, so every `data-r19`,
6
+ * `data-r22` and `data-r24` hook and every live behaviour stays as it was. Only the frame is new.
7
+ *
8
+ * Where each part comes from:
9
+ * - The two header actions are the list's own buttons, moved: "check now" (`data-r22="check"`, the local consistency check)
10
+ * and "show what would be sent" (`data-r19="plan"`, the first step of a TypeSafe run). Their labels are kept because the
11
+ * verdict line and the guide page name them.
12
+ * - The stat cards count the findings of the last TypeSafe run by rule (`broken-link`, `stale-after-passed`,
13
+ * `workflow-sweep`, `withheld-by-secret-scan`), read off the rows the list drew, so a card and the table always agree.
14
+ * The consent card reads `data-r19="consent-state"`. When no run exists, or the read has not answered, a card says so.
15
+ * - The table is the run's finding rows (`data-r19="finding"`), restyled as a grid. The board's "Status" column has no
16
+ * backing (a finding has no open or closed state), so the table shows the finding's severity instead.
17
+ * - The local consistency check and the TypeSafe settings have no place on the board. They are kept below the table as
18
+ * two cards, unchanged inside.
19
+ * In the fixture the list is empty, and the board says that validation is not read there.
20
+ */
21
+ import { el } from './dom.js';
22
+
23
+ const BTN = 'font:500 12.5px var(--sans);background:transparent;color:var(--foreground);border:1px solid var(--input);border-radius:8px;padding:0 12px;height:30px;min-height:30px;display:inline-grid;place-items:center;cursor:pointer;flex:none';
24
+ const BTNP = 'font:500 12.5px var(--sans);background:var(--foreground);color:var(--background);border:0;border-radius:8px;padding:0 12px;height:30px;min-height:30px;display:inline-grid;place-items:center;cursor:pointer;flex:none';
25
+ const BADGE = 'font:500 11px var(--sans);border:1px solid var(--border);border-radius:999px;padding:0 8px;line-height:18px;white-space:nowrap;display:inline-block';
26
+ const TONE = {
27
+ s: ';border-color:transparent;background:color-mix(in oklab, var(--success) 16%, transparent);color:var(--success)',
28
+ w: ';border-color:transparent;background:color-mix(in oklab, var(--warning) 16%, transparent);color:var(--warning)',
29
+ d: ';border-color:transparent;background:color-mix(in oklab, var(--destructive) 18%, transparent);color:var(--destructive)',
30
+ };
31
+ const STAT = 'flex:1 1 150px;min-width:0;border:1px solid var(--border);background:var(--background);border-radius:10px;padding:12px 14px';
32
+ const TH = 'font:500 10px var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--muted-foreground);text-align:left';
33
+ const COLS = 'display:grid;grid-template-columns:minmax(0,2.4fr) minmax(0,1fr) 92px minmax(0,1.3fr);gap:0 14px;align-items:center';
34
+ const CARD = 'border:1px solid var(--border);border-radius:10px;background:var(--background);padding:4px 0 8px;overflow:hidden';
35
+ const CARD_HEAD = 'padding:12px 14px 2px;font:600 15px var(--sans);color:var(--foreground)';
36
+ const LBL = 'font:500 10px var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--muted-foreground)';
37
+ const NOTE = 'color:var(--muted-foreground);font-size:13px;line-height:1.5';
38
+
39
+ const badge = (text, tone) => el('span', { style: BADGE + (tone ? TONE[tone] : '') }, String(text));
40
+
41
+ /** The board's stat cards. `kind` is what the card counts; the counts come from the rows the list drew. */
42
+ const STATS = [
43
+ { key: 'broken', label: 'Broken links', rule: 'broken-link', tone: 'var(--destructive)' },
44
+ { key: 'stale', label: 'Stale documents', rule: 'stale-after-passed', tone: 'var(--warning)' },
45
+ { key: 'workflow', label: 'Workflow defects', rule: 'workflow-sweep', tone: 'var(--warning)' },
46
+ { key: 'withheld', label: 'Withheld (credential shape)', rule: 'withheld-by-secret-scan', tone: null },
47
+ ];
48
+
49
+ /** What the list says about the last run: 'run' (the coverage line is drawn), 'none' (no run), or 'unread'. */
50
+ function runState(wrap) {
51
+ if (!wrap) return 'unread';
52
+ if (wrap.querySelector('[data-r19="coverage"]')) return 'run';
53
+ if (wrap.querySelector('[data-r19="no-run"]')) return 'none';
54
+ return 'unread';
55
+ }
56
+
57
+ function stats(wrap) {
58
+ const run = runState(wrap);
59
+ const rows = wrap ? [...wrap.querySelectorAll('[data-r19="finding"]')] : [];
60
+ const cards = STATS.map((s) => {
61
+ const n = rows.filter((r) => r.getAttribute('data-rule') === s.rule).length;
62
+ const value = run === 'run'
63
+ ? el('div', { 'data-board-value': String(n), style: 'font:600 24px var(--sans);line-height:1.3;color:' + (n > 0 && s.tone ? s.tone : 'var(--foreground)') }, String(n))
64
+ : el('div', { 'data-board-value': run, style: 'margin-top:6px' }, badge(run === 'none' ? 'no run yet' : 'not read'));
65
+ return el('div', { 'data-board-stat': s.key, style: STAT }, el('div', { style: 'color:var(--muted-foreground)' }, s.label), value);
66
+ });
67
+ const cs = wrap ? wrap.querySelector('[data-r19="consent-state"]') : null;
68
+ const txt = cs ? cs.textContent || '' : '';
69
+ const consent = !cs ? badge('not read') : /\bON\b/.test(txt) ? badge('given', 's') : badge('not given', 'w');
70
+ cards.push(el('div', { 'data-board-stat': 'consent', 'data-board-value': !cs ? 'unread' : /\bON\b/.test(txt) ? 'given' : 'not-given', style: STAT },
71
+ el('div', { style: 'color:var(--muted-foreground)' }, 'Jev consent'), el('div', { style: 'margin-top:6px' }, consent)));
72
+ return el('div', { 'data-board-part': 'stats', style: 'display:flex;gap:14px;flex-wrap:wrap' }, cards);
73
+ }
74
+
75
+ /** Split the list's children at its section labels. The labels are fixed text in modes-live.js. */
76
+ function sections(wrap) {
77
+ const kids = [...wrap.children];
78
+ const at = (re) => kids.findIndex((k) => re.test((k.textContent || '').trim()));
79
+ const iT = at(/^typesafe \(jev\)/i);
80
+ const iF = at(/^4 · findings/);
81
+ const end = (i, fallback) => (i < 0 ? fallback : i);
82
+ return {
83
+ consistency: kids.slice(0, end(iT, end(iF, kids.length))),
84
+ typesafe: iT < 0 ? [] : kids.slice(iT, end(iF, kids.length)),
85
+ findings: iF < 0 ? [] : kids.slice(iF),
86
+ };
87
+ }
88
+
89
+ /** Restyle one finding row as a table row. Only styles change: the row keeps its nodes, text, hooks and click. */
90
+ function asTableRow(row) {
91
+ row.setAttribute('style', COLS + ';width:100%;box-sizing:border-box;padding:10px 14px;text-align:left;border-bottom:1px solid var(--border);font-size:13px;color:var(--foreground);cursor:pointer;min-height:40px');
92
+ const [head, files] = row.children;
93
+ if (files) files.setAttribute('style', 'order:0;font:12px var(--mono);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--foreground)');
94
+ if (head) {
95
+ head.setAttribute('style', 'display:contents');
96
+ const [sev, rule, meta] = head.children;
97
+ if (rule) rule.setAttribute('style', 'order:1;font-size:13px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap');
98
+ if (sev) {
99
+ const v = (sev.textContent || '').trim();
100
+ const tone = v === 'high' ? TONE.d : v === 'medium' ? TONE.w : '';
101
+ sev.setAttribute('style', 'order:2;justify-self:start;' + BADGE + tone);
102
+ }
103
+ if (meta) meta.setAttribute('style', 'order:3;font:11px var(--mono);color:var(--muted-foreground);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap');
104
+ }
105
+ row.setAttribute('data-board-row', 'finding');
106
+ }
107
+
108
+ function findingsSection(parts) {
109
+ const [label, ...rest] = parts;
110
+ if (label) label.setAttribute('style', LBL + ';padding:0 2px');
111
+ const rows = rest.filter((n) => n.getAttribute && n.getAttribute('data-r19') === 'finding');
112
+ const notes = rest.filter((n) => !rows.includes(n));
113
+ for (const n of notes) {
114
+ const tone = /color:var\(--destructive\)/.test(n.getAttribute('style') || '') ? ';color:var(--destructive)' : '';
115
+ n.setAttribute('style', NOTE + tone + ';padding:0 2px');
116
+ }
117
+ rows.forEach(asTableRow);
118
+ const table = rows.length
119
+ ? el('div', { 'data-board-part': 'table', style: 'border:1px solid var(--border);border-radius:10px;overflow:hidden;background:var(--background)' },
120
+ el('div', { 'data-board-part': 'table-head', style: COLS + ';padding:10px 14px;border-bottom:1px solid var(--border)' },
121
+ el('span', { style: TH }, 'File and finding'), el('span', { style: TH }, 'Rule'), el('span', { style: TH }, 'Severity'), el('span', { style: TH }, 'Checked by')),
122
+ rows)
123
+ : null;
124
+ return el('div', { 'data-board-section': 'findings', style: 'display:flex;flex-direction:column;gap:8px' }, label || null, notes, table);
125
+ }
126
+
127
+ function card(key, title, parts) {
128
+ if (!parts.length) return null;
129
+ return el('div', { 'data-board-section': key, style: CARD }, el('div', { style: CARD_HEAD }, title), parts);
130
+ }
131
+
132
+ export function renderBoard(inner, ctx) {
133
+ const { FIXTURE, state } = ctx;
134
+ const repo = state.product || null;
135
+
136
+ const toolbar = el('div', { 'data-board-part': 'toolbar', style: 'display:flex;gap:8px;flex-wrap:wrap;align-items:center' });
137
+ const header = el('div', { 'data-board-part': 'header', style: 'display:flex;justify-content:space-between;align-items:flex-end;gap:12px;flex-wrap:wrap' },
138
+ el('div', null,
139
+ el('div', { 'data-board-title': '', style: 'font:600 22px var(--sans);line-height:1.45' }, 'Validate documents'),
140
+ el('div', { 'data-board-subtitle': '', style: 'color:var(--muted-foreground)' },
141
+ 'Local checks first. Nothing is sent anywhere until you consent for ' + (repo ? repo : 'this repository') + '.')),
142
+ toolbar);
143
+
144
+ const host = el('div', { 'data-board-section': 'list', style: 'display:flex;flex-direction:column;gap:14px' });
145
+ ctx.renderMode(host);
146
+ const wrap = host.querySelector('[data-r19="validate"]');
147
+
148
+ let statRow = null;
149
+ if (FIXTURE) {
150
+ statRow = el('div', { 'data-board-part': 'stats', style: NOTE }, 'Validation is not read in the fixture.');
151
+ } else if (wrap) {
152
+ statRow = stats(wrap);
153
+ // the two header actions are the list's own buttons, moved, so their hooks and presses stay as they were
154
+ const check = wrap.querySelector('[data-r22="check"]');
155
+ const plan = wrap.querySelector('[data-r19="plan"]');
156
+ if (check) { check.setAttribute('style', BTN); toolbar.appendChild(check); }
157
+ if (plan) { plan.setAttribute('style', BTNP); toolbar.appendChild(plan); }
158
+ const s = sections(wrap);
159
+ wrap.setAttribute('style', 'display:flex;flex-direction:column;gap:14px;padding:0');
160
+ const findings = s.findings.length ? findingsSection(s.findings) : null;
161
+ const foot = el('div', { 'data-board-part': 'note', style: NOTE }, 'Findings never edit a file. You decide what to fix.');
162
+ const cons = card('consistency', 'Consistency of this repository', s.consistency);
163
+ const ts = card('typesafe', 'TypeSafe (Jev)', s.typesafe);
164
+ // the consistency card keeps its own label, which the card head now says
165
+ if (s.consistency[0] && /^consistency of this repository/.test((s.consistency[0].textContent || '').trim())) s.consistency[0].style.display = 'none';
166
+ for (const n of [findings, foot, cons, ts]) if (n) wrap.appendChild(n);
167
+ }
168
+
169
+ inner.appendChild(el('div', { 'data-board': 'validate', style: 'display:flex;flex-direction:column;gap:14px;padding:4px 6px;font-size:13px;line-height:1.45;color:var(--foreground)' },
170
+ header, statRow, host));
171
+ }