create-pmos 0.250.0 → 0.251.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 +2 -2
- package/payload/planning/evals/eval-calibration.md +1 -1
- package/payload/planning/okrs/health-budgets.md +1 -1
- package/payload/scripts/workflow-state.js +3 -0
- package/payload/state/discoveries.md +1 -1
- package/payload/state/log.md +1 -1
- package/workspace/lib/engine/signals/facts.js +1 -1
- package/workspace/lib/engine/workflow-state.js +3 -0
- package/workspace/lib/vendor/scripts/workflow-state.js +3 -0
- package/workspace/package-lock.json +2 -2
- package/workspace/package.json +1 -1
- package/workspace/server.js +21 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.251.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
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
kernel-source: pmos@
|
|
2
|
-
assembled: 2026-10-
|
|
1
|
+
kernel-source: pmos@0657606
|
|
2
|
+
assembled: 2026-10-16
|
|
3
3
|
mode: vendor
|
|
@@ -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-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-16T03:26:16Z"}
|
|
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-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-16T03:26:16Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -716,6 +716,9 @@ function computeWorkflowDerived(id, opts) {
|
|
|
716
716
|
}
|
|
717
717
|
return best;
|
|
718
718
|
}
|
|
719
|
+
// A caller that never reads `last_activity` (the facts reader) says so, and the subprocess is not started: one per
|
|
720
|
+
// initiative was 9.5 of the 12 seconds a cold facts read took on this repository.
|
|
721
|
+
if (opts && opts.skipLastActivity) return null;
|
|
719
722
|
const out = git(root, ['log', '-1', '--format=%ct', '--', ...paths]);
|
|
720
723
|
const t = out && parseInt(out.trim(), 10);
|
|
721
724
|
return Number.isFinite(t) ? t : null;
|
|
@@ -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-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-16T03:26:16Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
package/payload/state/log.md
CHANGED
|
@@ -92,7 +92,7 @@ function readRepoFacts(root, opts) {
|
|
|
92
92
|
const inis = [];
|
|
93
93
|
for (const id of ws.listInitiatives(base)) {
|
|
94
94
|
let w;
|
|
95
|
-
try { w = ws.computeWorkflow(id, range ? { root: base, range } : { root: base }); } catch { continue; }
|
|
95
|
+
try { w = ws.computeWorkflow(id, range ? { root: base, range, skipLastActivity: true } : { root: base, skipLastActivity: true }); } catch { continue; }
|
|
96
96
|
if (!w || !w.ok) continue;
|
|
97
97
|
const fm = readFrontmatter(path.join(base, w.record || `planning/initiatives/${id}.md`));
|
|
98
98
|
inis.push({
|
|
@@ -716,6 +716,9 @@ function computeWorkflowDerived(id, opts) {
|
|
|
716
716
|
}
|
|
717
717
|
return best;
|
|
718
718
|
}
|
|
719
|
+
// A caller that never reads `last_activity` (the facts reader) says so, and the subprocess is not started: one per
|
|
720
|
+
// initiative was 9.5 of the 12 seconds a cold facts read took on this repository.
|
|
721
|
+
if (opts && opts.skipLastActivity) return null;
|
|
719
722
|
const out = git(root, ['log', '-1', '--format=%ct', '--', ...paths]);
|
|
720
723
|
const t = out && parseInt(out.trim(), 10);
|
|
721
724
|
return Number.isFinite(t) ? t : null;
|
|
@@ -716,6 +716,9 @@ function computeWorkflowDerived(id, opts) {
|
|
|
716
716
|
}
|
|
717
717
|
return best;
|
|
718
718
|
}
|
|
719
|
+
// A caller that never reads `last_activity` (the facts reader) says so, and the subprocess is not started: one per
|
|
720
|
+
// initiative was 9.5 of the 12 seconds a cold facts read took on this repository.
|
|
721
|
+
if (opts && opts.skipLastActivity) return null;
|
|
719
722
|
const out = git(root, ['log', '-1', '--format=%ct', '--', ...paths]);
|
|
720
723
|
const t = out && parseInt(out.trim(), 10);
|
|
721
724
|
return Number.isFinite(t) ? t : null;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmos-workspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.251.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.251.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@xterm/addon-fit": "^0.11.0",
|
package/workspace/package.json
CHANGED
package/workspace/server.js
CHANGED
|
@@ -5873,13 +5873,30 @@ const TYPES = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; ch
|
|
|
5873
5873
|
const zlib = require('zlib');
|
|
5874
5874
|
const GZIP_MIN_BYTES = 1024;
|
|
5875
5875
|
const GZIP_TYPES = /^(?:text\/|application\/(?:json|javascript|manifest\+json)|image\/svg\+xml)/;
|
|
5876
|
-
|
|
5876
|
+
// Whether this client takes a gzip body: a `gzip` (or `*`) coding in Accept-Encoding whose q-value is not 0. `gzip;q=0` is a
|
|
5877
|
+
// refusal. PMOS_NO_COMPRESS=1 turns compression off, so a proxy or client that mishandles it can be ruled out in one restart.
|
|
5878
|
+
const NO_COMPRESS = process.env.PMOS_NO_COMPRESS === '1';
|
|
5879
|
+
function wantsGzip(res) {
|
|
5880
|
+
if (NO_COMPRESS) return false;
|
|
5881
|
+
const q = res && res.req;
|
|
5882
|
+
const h = q && q.headers && q.headers['accept-encoding'];
|
|
5883
|
+
if (!h) return false;
|
|
5884
|
+
for (const part of String(h).split(',')) {
|
|
5885
|
+
const [coding, ...params] = part.trim().toLowerCase().split(';');
|
|
5886
|
+
if (coding !== 'gzip') continue;
|
|
5887
|
+
const qv = params.map((x) => x.trim()).find((x) => x.startsWith('q='));
|
|
5888
|
+
return !(qv && Number(qv.slice(2)) === 0);
|
|
5889
|
+
}
|
|
5890
|
+
return false;
|
|
5891
|
+
}
|
|
5877
5892
|
const fileMemo = new Map(); // file -> { sig, buf, etag, gz }
|
|
5878
5893
|
const FILE_MEMO_MAX = 400;
|
|
5879
5894
|
function serveFile(res, file, type, noCache) {
|
|
5880
5895
|
fs.stat(file, (serr, st) => {
|
|
5881
5896
|
if (serr || !st.isFile()) { res.writeHead(404); res.end('not found'); return; }
|
|
5882
|
-
|
|
5897
|
+
// size, mtime, change time and inode: a package install stamps every file with one fixed mtime, so size and mtime alone
|
|
5898
|
+
// would keep serving the old bytes after a same-size edit.
|
|
5899
|
+
const sig = `${st.size}:${st.mtimeMs}:${st.ctimeMs}:${st.ino}`;
|
|
5883
5900
|
const send = (m) => {
|
|
5884
5901
|
const ctype = type || TYPES[path.extname(file)] || 'application/octet-stream';
|
|
5885
5902
|
const headers = { 'Content-Type': ctype, 'ETag': m.etag };
|
|
@@ -5902,7 +5919,7 @@ function serveFile(res, file, type, noCache) {
|
|
|
5902
5919
|
if (hit && hit.sig === sig) return send(hit);
|
|
5903
5920
|
fs.readFile(file, (err, buf) => {
|
|
5904
5921
|
if (err) { res.writeHead(404); res.end('not found'); return; }
|
|
5905
|
-
const m = { sig, buf, etag: `
|
|
5922
|
+
const m = { sig, buf, etag: `"${require('crypto').createHash('sha1').update(buf).digest('hex').slice(0, 20)}"`, gz: null };
|
|
5906
5923
|
const ctype = type || TYPES[path.extname(file)] || '';
|
|
5907
5924
|
const finish = () => { if (fileMemo.size >= FILE_MEMO_MAX) fileMemo.clear(); fileMemo.set(file, m); send(m); };
|
|
5908
5925
|
if (buf.length >= GZIP_MIN_BYTES && GZIP_TYPES.test(ctype)) zlib.gzip(buf, (e, gz) => { if (!e) m.gz = gz; finish(); });
|
|
@@ -6021,6 +6038,7 @@ function json(res, obj, code = 200) {
|
|
|
6021
6038
|
if (body.length >= GZIP_MIN_BYTES && wantsGzip(res)) {
|
|
6022
6039
|
// Async, so a 1 MB library answer costs the loop nothing beyond the stringify (workspace-event-loop-lag).
|
|
6023
6040
|
return zlib.gzip(body, (e, gz) => {
|
|
6041
|
+
if (res.headersSent || res.writableEnded || res.destroyed) return; // the handler or the client got there first
|
|
6024
6042
|
if (e) { res.writeHead(code, { 'Content-Type': 'application/json' }); res.end(body); return; }
|
|
6025
6043
|
res.writeHead(code, { 'Content-Type': 'application/json', 'Content-Encoding': 'gzip', 'Vary': 'Accept-Encoding', 'Content-Length': gz.length });
|
|
6026
6044
|
res.end(gz);
|