create-pmos 0.233.0 → 0.234.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/lib/calendar-events.js +31 -4
- package/workspace/package-lock.json +2 -2
- package/workspace/package.json +1 -1
- package/workspace/skills/calendar-sync.skill +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.234.0",
|
|
4
4
|
"description": "Scaffold the PMOS portable kit — run the PMOS product-management workflow (initiative → lane-fit spec → contract → build → three gates, with work-type lanes for features, research, maintenance, incidents, and content) entirely inside your own repo, with all state as git-versioned files. Includes `workflow-sweep`, which audits the state every board derives from and separates real defects from work you simply have not done. `create-pmos workspace` also installs + launches the local Terminal Workspace GUI. No database, no network, no telemetry.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
package/payload/KIT-VERSION
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Eval calibration log"
|
|
4
4
|
description: "Append-only record of every PM override of an evaluator verdict."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-14T09:
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-14T09:51:20Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Health budgets"
|
|
4
4
|
description: "Closed registry of health-budget anchors (D59), edited only by the PM."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-14T09:
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-14T09:51:20Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Discoveries and friction log"
|
|
4
4
|
description: "Append-only record of what runs discovered and where the process rubbed."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-14T09:
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-14T09:51:20Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* meetings came in with the morning brief saw them under Record and nowhere else. This reader joins the three. The Record menu reads it
|
|
12
12
|
* too (todayOf, GET /api/calendar-today): it read the Agenda once, so an imported meeting was on the Calendar and missing under Record. An imported event wins
|
|
13
13
|
* over a remembered one with the same start minute and title, because it carries the PM's notes; today's brief replaces the remembered
|
|
14
|
-
* events of today, as agendaMonth does.
|
|
14
|
+
* events of today, as agendaMonth does. Two imported copies of one meeting (same start minute and title, different ids) are one
|
|
15
|
+
* event (oneEach), so the Calendar does not draw a meeting twice side by side.
|
|
15
16
|
*
|
|
16
17
|
* `at` and `end` of a remembered event are local date-times with the offset (2026-10-12T10:45:00+08:00), so a reader that takes the
|
|
17
18
|
* first ten characters as the day (calendar-model.js bucketEvents) gets the local day. READ ONLY; a file that cannot be read gives nothing. */
|
|
@@ -52,6 +53,32 @@ function asEvent(e, src) {
|
|
|
52
53
|
...(e.series ? { series: String(e.series) } : {}) };
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
/** One imported event per meeting. The ledger folds by event id, and the id is whatever the agent session that read the calendar
|
|
57
|
+
* gave (calendar-sync.skill): one run can give the calendar's event id and the next its iCal UID, or the same meeting can come in
|
|
58
|
+
* from two calendars. Each copy was then a meeting of its own, and the Calendar drew the two side by side. Copies with the same
|
|
59
|
+
* start minute and title are one meeting: the copy read last is kept (its title and times are the newest), and the PM's notes of
|
|
60
|
+
* every copy stay on it. Events with no readable start are kept as they are. Pure. */
|
|
61
|
+
function oneEach(imported) {
|
|
62
|
+
const by = new Map();
|
|
63
|
+
const loose = [];
|
|
64
|
+
imported.forEach((e, i) => {
|
|
65
|
+
const s = e && e.at ? Date.parse(e.at) : NaN;
|
|
66
|
+
if (!e || Number.isNaN(s)) { if (e) loose.push(e); return; }
|
|
67
|
+
const k = minuteKey(s, e.title);
|
|
68
|
+
const prev = by.get(k);
|
|
69
|
+
if (!prev) { by.set(k, { e, i }); return; }
|
|
70
|
+
const newer = String(e.fetched_at || '') > String(prev.e.fetched_at || '') || (String(e.fetched_at || '') === String(prev.e.fetched_at || '') && i > prev.i);
|
|
71
|
+
const keep = newer ? e : prev.e;
|
|
72
|
+
const other = newer ? prev.e : e;
|
|
73
|
+
const notes = [].concat(prev.e.pm_notes || [], e.pm_notes || []);
|
|
74
|
+
const ids = new Set();
|
|
75
|
+
const pm = notes.filter((x) => { const id = x && (x.id || x.at + '|' + x.text); if (ids.has(id)) return false; ids.add(id); return true; })
|
|
76
|
+
.sort((a, b) => String(a && a.at).localeCompare(String(b && b.at)));
|
|
77
|
+
by.set(k, { e: { ...keep, pm_notes: pm, agent_prep_notes: keep.agent_prep_notes || other.agent_prep_notes || '', initiative: keep.initiative || other.initiative || null }, i: newer ? i : prev.i });
|
|
78
|
+
});
|
|
79
|
+
return [...by.values()].map((x) => x.e).concat(loose);
|
|
80
|
+
}
|
|
81
|
+
|
|
55
82
|
function readAll(runtimeDir, { now = Date.now() } = {}) {
|
|
56
83
|
if (!runtimeDir) return [];
|
|
57
84
|
let imported = [];
|
|
@@ -62,8 +89,8 @@ function readAll(runtimeDir, { now = Date.now() } = {}) {
|
|
|
62
89
|
const briefed = brief && brief.date === today && Array.isArray(brief.calendar) ? brief.calendar.map((e) => asEvent(e, 'brief')).filter(Boolean) : null;
|
|
63
90
|
let remembered = (mem && Array.isArray(mem.events) ? mem.events : []).map((e) => asEvent(e, 'memory')).filter(Boolean);
|
|
64
91
|
if (briefed) remembered = remembered.filter((e) => e.at.slice(0, 10) !== today).concat(briefed);
|
|
65
|
-
const
|
|
66
|
-
const
|
|
92
|
+
const out = oneEach(imported);
|
|
93
|
+
const seen = new Set(out.filter((e) => e && e.at && !Number.isNaN(Date.parse(e.at))).map((e) => minuteKey(Date.parse(e.at), e.title)));
|
|
67
94
|
for (const e of remembered) {
|
|
68
95
|
const k = minuteKey(Date.parse(e.at), e.title);
|
|
69
96
|
if (seen.has(k)) continue;
|
|
@@ -92,4 +119,4 @@ const eventsOn = (events, day) => listOf(events).filter((e) => e.date === day);
|
|
|
92
119
|
/** Today's meetings (the Record menu): eventsOn the local day of `now`. */
|
|
93
120
|
const todayOf = (events, now = Date.now()) => eventsOn(events, localDate(now));
|
|
94
121
|
|
|
95
|
-
module.exports = { readAll, asEvent, localIso, todayOf, eventsOn, listOf, menuShape };
|
|
122
|
+
module.exports = { readAll, oneEach, asEvent, localIso, todayOf, eventsOn, listOf, menuShape };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmos-workspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.234.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pmos-workspace",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.234.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@xterm/addon-fit": "^0.11.0",
|
package/workspace/package.json
CHANGED
|
@@ -24,6 +24,7 @@ Procedure:
|
|
|
24
24
|
1. List the PM's events from the start of today to the end of the 14th day after today, from the primary calendar. Fetch every page. Skip an event the PM declined.
|
|
25
25
|
2. Write the events as JSON in the scratch folder, for example `<scratch>/calendar.json`, as a list with one object per event:
|
|
26
26
|
`{"id", "title", "start", "end", "attendees": ["Display Name", …], "initiative": "<initiative id or null>"}`
|
|
27
|
+
`id` is the calendar's own id for that event (for a repeating meeting, the id of that one occurrence). Use the same id on every run, never a made-up one: PMOS keeps one copy per id.
|
|
27
28
|
`start` and `end` are ISO times with the offset, for example `2026-10-12T09:00:00+07:00`. An all-day event has the date only.
|
|
28
29
|
3. Run `pmos calendar import --from <scratch>/calendar.json --sensitivity internal`. If it refuses the file, fix what it names and run it once more.
|
|
29
30
|
4. Delete the scratch file.
|