create-pmos 0.239.0 → 0.241.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/KIT-VERSION +1 -1
- package/payload/planning/evals/eval-calibration.md +1 -1
- package/payload/planning/okrs/health-budgets.md +1 -1
- package/payload/state/discoveries.md +1 -1
- package/workspace/c3-report-structure-real-test.js +211 -0
- package/workspace/c3-report-structure-test.js +382 -0
- package/workspace/guide/briefing.md +31 -3
- package/workspace/guide/index.md +2 -0
- package/workspace/guide/report-design.md +4 -0
- package/workspace/guide/report-structure.md +91 -0
- package/workspace/guide/reports-dashboards.md +1 -1
- package/workspace/guide/reports-set-up.md +2 -0
- package/workspace/lib/charts-store.js +124 -0
- package/workspace/lib/headless.js +17 -3
- package/workspace/lib/needs-providers.js +1 -0
- package/workspace/lib/report-structure-needs.js +64 -0
- package/workspace/lib/report-structure.js +515 -0
- package/workspace/lib/route-modules.js +3 -1
- package/workspace/lib/routes-brief-page.js +56 -5
- package/workspace/lib/routes-mentions.js +37 -0
- package/workspace/lib/routes-report-setups.js +11 -6
- package/workspace/lib/routes-report-structure.js +619 -0
- package/workspace/package-lock.json +2 -2
- package/workspace/package.json +1 -1
- package/workspace/public/pm/briefing-deck.js +1 -0
- package/workspace/public/pm/c3-brief-mentions.js +136 -0
- package/workspace/public/pm/c3-brief-needs.js +23 -0
- package/workspace/public/pm/c3-brief-pulse.js +1 -0
- package/workspace/public/pm/c3-brief-summary.js +139 -0
- package/workspace/public/pm/c3-demo-brief-page.js +20 -4
- package/workspace/public/pm/c3-demo-rd.js +4 -0
- package/workspace/public/pm/c3-design-studio.js +7 -3
- package/workspace/public/pm/c3-report-setup.js +3 -0
- package/workspace/public/pm/c3-report-structure.css +40 -0
- package/workspace/public/pm/c3-report-structure.js +455 -0
- package/workspace/public/pm/c3-reports.css +2 -0
- package/workspace/public/pm/c3-reports.js +22 -3
- package/workspace/public/pm/c3-shell.js +34 -2
- package/workspace/public/pm/console3.css +12 -0
- package/workspace/server.js +38 -9
- package/workspace/skills/report-source-check.skill +45 -0
- package/workspace/skills/report-structure-dry.skill +51 -0
- package/workspace/skills/report-structure.skill +61 -0
- package/workspace/skills/report-template.skill +10 -0
- package/workspace/test-files.json +2 -0
- package/workspace/test-lists/report-structure-backend.json +5 -0
- package/workspace/test-lists/report-structure-ui.json +11 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/* routes-mentions.js — POST /api/mentions/read { id, read } → { ok } (initiative c3-brief-summary-mentions)
|
|
3
|
+
*
|
|
4
|
+
* The PM marked a mention of today's brief read on the Briefing (read: false undoes it). One appended line in <runtime>/mentions/read.jsonl:
|
|
5
|
+
* { id, read, at }. GET /api/brief-page reads the file back (routes-brief-page.js readMarks: the last line for an id wins).
|
|
6
|
+
* The page press only (pm-guard requirePagePress). Nothing is sent to Slack, Jira, Confluence or mail. */
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const guard = require('./pm-guard.js');
|
|
9
|
+
const pmItems = require('./pm-items.js');
|
|
10
|
+
const { MENTION_ID } = require('./routes-brief-page.js');
|
|
11
|
+
|
|
12
|
+
/** The check of a body: null when it is good, else the reason. Pure. */
|
|
13
|
+
function badBody(b) {
|
|
14
|
+
if (!b || !MENTION_ID.test(String(b.id || ''))) return 'id must be a mention id (12 hex characters)';
|
|
15
|
+
if (typeof b.read !== 'boolean') return 'read must be true or false';
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
prefixes: ['/api/mentions'],
|
|
21
|
+
badBody,
|
|
22
|
+
handle: async (req, res, url, ctx) => {
|
|
23
|
+
if (url.pathname !== '/api/mentions/read') return false;
|
|
24
|
+
if (req.method !== 'POST') { ctx.json(res, { ok: false, error: 'POST only' }, 405); return true; }
|
|
25
|
+
const got = await ctx.readJson(req);
|
|
26
|
+
if (!got.ok) { ctx.json(res, { ok: false, error: 'invalid JSON body' }, 400); return true; }
|
|
27
|
+
const b = got.body || {};
|
|
28
|
+
const g = guard.requirePagePress(req, b);
|
|
29
|
+
if (!g.ok) { ctx.json(res, { ok: false, error: g.error }, g.status); return true; }
|
|
30
|
+
const err = badBody(b);
|
|
31
|
+
if (err) { ctx.json(res, { ok: false, error: err }, 400); return true; }
|
|
32
|
+
if (!ctx.runtimeDir) { ctx.json(res, { ok: false, error: 'no runtime folder' }, 500); return true; }
|
|
33
|
+
pmItems.appendEvent(path.join(ctx.runtimeDir, 'mentions', 'read.jsonl'), { id: b.id, read: b.read }, new Date());
|
|
34
|
+
ctx.json(res, { ok: true });
|
|
35
|
+
return true;
|
|
36
|
+
},
|
|
37
|
+
};
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
/* routes-report-setups.js — the Reports workspace's routes (initiative report-workspace, PRD sections 2 to 6).
|
|
3
3
|
*
|
|
4
4
|
* GET /api/report-setups?ws → { setups: [{ id, name, purpose, audience, source, output, schedule, label, next_draft, last_issued, status, cycles,
|
|
5
|
-
* copy: { kind, state: none|in-step|pmos-ahead|copy-changed|not-checked, url }
|
|
5
|
+
* copy: { kind, state: none|in-step|pmos-ahead|copy-changed|not-checked, url },
|
|
6
|
+
* structure: { saved, at, charts, sections } | null the saved report structure (initiative report-structure) }],
|
|
6
7
|
* findings: [{ id, name, why, evidence: [{ kind, text }], confidence: likely|not-sure, proposal }],
|
|
7
8
|
* asks: [{ id, text, state: running|ready|failed, changed, proposal, error }],
|
|
8
9
|
* finder: { cadence, sources: { calendar, slack, mail, drive }, last_run, next_run } }
|
|
@@ -119,7 +120,10 @@ function documentsOf(ctx, w) {
|
|
|
119
120
|
return { docs, reports: r.reports };
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
|
|
123
|
+
/** The saved structure of a report (initiative report-structure): { saved, at, charts, sections }, else null. */
|
|
124
|
+
function structureOf(root, id) { try { return require('./report-structure.js').savedOf(root, id); } catch { return null; } }
|
|
125
|
+
|
|
126
|
+
function setupOut(s, now, docsOf, idx, root) {
|
|
123
127
|
const mine = (docsOf.reports || []).filter((x) => x.cadence === s.id);
|
|
124
128
|
const issued = mine.filter((x) => x.issued).map((x) => x.slot).sort().pop() || null;
|
|
125
129
|
const latest = mine.slice().sort((a, b) => (a.slot < b.slot ? 1 : -1))[0] || null;
|
|
@@ -129,6 +133,7 @@ function setupOut(s, now, docsOf, idx) {
|
|
|
129
133
|
id: s.id, name: s.name, purpose: s.purpose, audience: s.audience, source: s.source, output: s.output, schedule: s.schedule, label: RS.scheduleLabel(s.schedule),
|
|
130
134
|
next_draft: next, last_issued: issued, status: s.status, cycles: mine.length,
|
|
131
135
|
copy: copyOf(latest ? linkFor(idx, latest.path, s.output) : null, docSync.copyKindOf(s.output)),
|
|
136
|
+
structure: structureOf(root, s.id),
|
|
132
137
|
};
|
|
133
138
|
}
|
|
134
139
|
|
|
@@ -157,7 +162,7 @@ function listAnswer(ctx, w, now) {
|
|
|
157
162
|
const docsOf = documentsOf(ctx, w);
|
|
158
163
|
const idx = copyIndex(w.root);
|
|
159
164
|
return {
|
|
160
|
-
setups: [...st.setups.values()].map((s) => setupOut(s, new Date(now), docsOf, idx)),
|
|
165
|
+
setups: [...st.setups.values()].map((s) => setupOut(s, new Date(now), docsOf, idx, w.root)),
|
|
161
166
|
findings: RS.openFindings(st).map((f) => ({ id: f.id, name: f.name, why: f.why, evidence: f.evidence, confidence: f.confidence, proposal: f.proposal })),
|
|
162
167
|
asks: [...st.asks.values()].map((a) => ({ id: a.id, text: a.text, state: a.state, changed: !!a.changed, proposal: a.proposal, error: a.error })),
|
|
163
168
|
finder: finderOut(ctx, st, now),
|
|
@@ -341,7 +346,7 @@ async function handle(req, res, url, ctx) {
|
|
|
341
346
|
// in place and says why; Draft it now on the row tries again.
|
|
342
347
|
const drafted = firstDraft(root, r, g.actor);
|
|
343
348
|
if (drafted.path) ctx.invalidateNeedsAgg && ctx.invalidateNeedsAgg();
|
|
344
|
-
return send({ status: 200, body: { ok: true, setup: setupOut(r.setup, new Date(), documentsOf(ctx, w), copyIndex(root)), first_draft: r.first_draft, drafted } });
|
|
349
|
+
return send({ status: 200, body: { ok: true, setup: setupOut(r.setup, new Date(), documentsOf(ctx, w), copyIndex(root), root), first_draft: r.first_draft, drafted } });
|
|
345
350
|
}
|
|
346
351
|
if (sub === 'dismiss') {
|
|
347
352
|
const r = RS.dismissFinding(file, { id: text(body.finding), by: g.actor });
|
|
@@ -363,7 +368,7 @@ async function handle(req, res, url, ctx) {
|
|
|
363
368
|
if (sub === 'pause' || sub === 'resume') {
|
|
364
369
|
const r = RS[sub](root, text(body.id), opts);
|
|
365
370
|
if (!r.ok) return fail(r);
|
|
366
|
-
return send({ status: 200, body: { ok: true, setup: setupOut(r.setup, new Date(), documentsOf(ctx, w), copyIndex(root)) } });
|
|
371
|
+
return send({ status: 200, body: { ok: true, setup: setupOut(r.setup, new Date(), documentsOf(ctx, w), copyIndex(root), root) } });
|
|
367
372
|
}
|
|
368
373
|
if (sub === 'from-dashboard') {
|
|
369
374
|
const key = text(body.key);
|
|
@@ -376,7 +381,7 @@ async function handle(req, res, url, ctx) {
|
|
|
376
381
|
schedule: body.schedule, sources: [`The ${name} dashboard`], sections: RS.FRAME.slice(), learned_from: 'Set up from the dashboard.', fixes: [] };
|
|
377
382
|
const r = RS.setUp(root, proposal, opts);
|
|
378
383
|
if (!r.ok) return fail(r);
|
|
379
|
-
return send({ status: 200, body: { ok: true, setup: setupOut(r.setup, new Date(), { reports: [] }, new Map()) } });
|
|
384
|
+
return send({ status: 200, body: { ok: true, setup: setupOut(r.setup, new Date(), { reports: [] }, new Map(), root) } });
|
|
380
385
|
}
|
|
381
386
|
if (sub === 'finder') {
|
|
382
387
|
const r = RS.setFinder(file, { cadence: body.cadence, sources: body.sources, by: g.actor });
|