create-pmos 0.120.0 → 0.122.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-pmos",
3
- "version": "0.120.0",
3
+ "version": "0.122.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": {
@@ -1,3 +1,3 @@
1
- kernel-source: pmos@aed5805
2
- assembled: 2026-10-08
1
+ kernel-source: pmos@7af87c4
2
+ assembled: 2026-10-09
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-08T09:05:35Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-09T01:31:23Z"}
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-08T09:05:35Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-09T01:31:23Z"}
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-08T09:05:35Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-09T01:31:23Z"}
6
6
  status: stable
7
7
  ---
8
8
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  Newest first, ISO dates.
4
4
 
5
- ## 2026-10-08
5
+ ## 2026-10-09
6
6
 
7
7
  - Adopted OKF v0.2 (D73).
@@ -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-04
5
+ timestamp: 2026-10-08
6
6
  ---
7
7
 
8
8
  # Guide: initiatives and next steps
@@ -73,7 +73,7 @@ At most 3 prepared sessions run at once and 8 start each day. Go refuses a start
73
73
 
74
74
  ### Sources and the typed judgment option
75
75
 
76
- Choose a profile in the plan. The session may read only the sources that profile declares and that suit the step: Wispr Flow notes, Gmail, Google Calendar, Confluence, Jira and Slack. They are read-only.
76
+ The plan starts with your default Claude profile from Settings. Choose another profile, or none, in the plan. Prepare again and Hand to agent start under the same default. The session may read only the sources that profile declares and that suit the step: Wispr Flow notes, Gmail, Google Calendar, Confluence, Jira and Slack. They are read-only.
77
77
 
78
78
  You can turn on the Typesafe skill for a run. It helps the session categorise, score and decide yes or no with a typed answer. It sends the question text to an outside service and needs `TYPESAFE_API_KEY` in the session. It is off by default. The session prints each answer as a `JUDGMENT` line, and the review checks its shape.
79
79
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pmos-workspace",
3
- "version": "0.120.0",
3
+ "version": "0.122.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pmos-workspace",
9
- "version": "0.120.0",
9
+ "version": "0.122.0",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@xterm/addon-fit": "^0.11.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmos-workspace",
3
- "version": "0.120.0",
3
+ "version": "0.122.0",
4
4
  "private": true,
5
5
  "description": "PMOS Terminal Workspace — local companion hosting real PTYs beside the library and gates (D62)",
6
6
  "license": "MIT",
@@ -15,6 +15,7 @@
15
15
  import { el } from './dom.js';
16
16
  import { state, setState, FIXTURE } from './state.js';
17
17
  import { askText } from './dialogs.js';
18
+ import { adoptProfileDefault } from './dock.js';
18
19
 
19
20
  let dataMod = null;
20
21
  const dataReady = import('./data.js').then((m) => { dataMod = m; }).catch(() => { dataMod = null; });
@@ -30,14 +31,17 @@ const iv = (t) => el('span', null, t == null ? '' : String(t));
30
31
  const ui = Object.create(null);
31
32
  let profileList = null;
32
33
  const touch = () => setState((s) => ({ nsTick: (s.nsTick || 0) + 1 }));
33
- const slot = (id) => (ui[id] = ui[id] || { judgment: false, profile: '' });
34
+ const slot = (id) => (ui[id] = ui[id] || { judgment: false, profile: undefined });
34
35
 
35
36
  async function prepare(slug, it) {
36
37
  const u = slot(it.id);
37
38
  u.loading = true; u.note = null; touch();
38
39
  await dataReady;
39
- if (profileList === null) { const r = await dataMod.nextStepProfiles(); profileList = (r && r.profiles) || []; }
40
- const out = await dataMod.nextStepPost('prepare', { initiative_id: slug, item_id: it.id, agent: 'claude', profile: u.profile || undefined, judgment: u.judgment });
40
+ // Read every time: the default profile is the server's, and the list can change while the page is open.
41
+ const rd = await dataMod.nextStepProfiles(); profileList = (rd && rd.profiles) || [];
42
+ // Until the PM picks one here, the step starts under the default Claude profile (Settings). 'none' is a choice, sent as null.
43
+ if (u.profile === undefined) u.profile = adoptProfileDefault(rd) || '';
44
+ const out = await dataMod.nextStepPost('prepare', { initiative_id: slug, item_id: it.id, agent: 'claude', profile: u.profile || null, judgment: u.judgment });
41
45
  u.loading = false;
42
46
  u.plan = out && out.ok ? out : null;
43
47
  if (!out || out.ok === false) u.note = { tone: 'bad', text: (out && out.error) || 'could not prepare' };
@@ -47,7 +51,7 @@ async function go(slug, it) {
47
51
  const u = slot(it.id); const pl = u.plan;
48
52
  if (!pl || !pl.prompt_sha) return;
49
53
  u.loading = true; u.note = null; touch();
50
- const out = await dataMod.nextStepPost('go', { initiative_id: slug, item_id: it.id, agent: 'claude', profile: u.profile || undefined, judgment: u.judgment, confirm: true, prompt_sha: pl.prompt_sha });
54
+ const out = await dataMod.nextStepPost('go', { initiative_id: slug, item_id: it.id, agent: 'claude', profile: u.profile || null, judgment: u.judgment, confirm: true, prompt_sha: pl.prompt_sha });
51
55
  u.loading = false;
52
56
  if (!out || out.ok === false) { u.note = { tone: 'bad', text: (out && out.error) || 'nothing was started' }; if (out && out.prompt_sha) u.plan = null; touch(); return; }
53
57
  u.plan = null; u.note = { tone: 'ok', text: 'started — ' + (out.notice || out.plan.why || 'the session is running') };
@@ -462,6 +462,13 @@ function writePrefs(prefs) {
462
462
  try { fs.writeFileSync(PREFS_FILE, JSON.stringify(prefs)); return null; }
463
463
  catch (e) { warnWriteFail('prefs.json (profile preselection)', e); return `the preference could not be saved — ${String((e && e.code) || 'write failed')}`; }
464
464
  }
465
+ // The default profile for an agent, for a start the PM pressed that named no profile at all (`undefined`). An explicit
466
+ // null means "no profile" and is never replaced. A remembered id that no longer reads yields undefined: the start then
467
+ // runs with no profile and says nothing it did not do, rather than failing on a preference the PM did not just choose.
468
+ function defaultProfileFor(agent) {
469
+ const id = readPrefs()[agent];
470
+ return typeof id === 'string' && id && resolveProfileForSpawn(id).ok ? id : undefined;
471
+ }
465
472
  // The merged environment a session actually runs under. The overlay goes ON TOP of sessionEnv(),
466
473
  // AFTER the marker-stripping (R3) — never instead of it, or a profile would silently re-enable the
467
474
  // nested-session markers sessionEnv exists to remove.
@@ -3335,6 +3342,9 @@ function spawnSession({ name, agent, cwd, ws, origin, initialInput, profile, ini
3335
3342
  // `pres.id` is null whenever the overlay was not applied — including when a profile was asked for
3336
3343
  // and could not be read — so the pane line can never name a profile that did nothing (R3/R9).
3337
3344
  // `profileRefusal` is server-internal: callers that can refuse read it and say why.
3345
+ // A start that names no profile (`undefined`) gets the agent's default Claude profile, whoever started it: a skill, a
3346
+ // schedule, a note, a doc check. Only the composer used to apply it, so every other start ran under Claude's own config.
3347
+ if (profile === undefined && !isShell) profile = defaultProfileFor(agent);
3338
3348
  const pres = resolveProfileForSpawn(profile);
3339
3349
  const penv = sessionEnvWithProfile(pres.profile);
3340
3350
  // sessionEnvWithProfile returns a fresh object (sessionEnv copies process.env), so these touch this session only.
@@ -3486,6 +3496,8 @@ function spawnWithBranch({ name, agent, cwd, ws, branch, createBranch, isolate,
3486
3496
  // The profile is checked BEFORE any git operation. A composer that picked a profile PMOS cannot
3487
3497
  // read must not leave a branch checked out and a session running under the wrong environment —
3488
3498
  // and a session that claims a profile it never got is the quietest failure this feature has.
3499
+ // `undefined` is "not asked", so it takes the agent's default; the resolved id below is explicit, so spawnSession adds none.
3500
+ if (profile === undefined && agent && agent !== 'shell') profile = defaultProfileFor(agent);
3489
3501
  const pres = resolveProfileForSpawn(profile);
3490
3502
  if (!pres.ok) return { ok: false, error: pres.error };
3491
3503
  // The model choice is checked on the same terms (D75): a refused choice starts nothing, and leaves no branch moved.
@@ -3738,6 +3750,8 @@ function nextStepsFor(w, id) {
3738
3750
  }
3739
3751
  // Everything a prepare shows, computed WITHOUT creating or writing anything. `go` recomputes it and compares the hash.
3740
3752
  function prepareNextStep(body) {
3753
+ // A request that names no profile gets the default Claude profile; `profile: null` is the PM choosing none.
3754
+ if (body.profile === undefined) { const d = defaultProfileFor(body.agent || 'claude'); if (d) body = { ...body, profile: d }; }
3741
3755
  const w = getWs(body.ws);
3742
3756
  if (!w) return { ok: false, status: 400, error: 'no workspace' };
3743
3757
  const id = String(body.initiative_id || '');
@@ -7446,7 +7460,7 @@ async function handleRequest(req, res) {
7446
7460
  const budget = handoffBudget();
7447
7461
  if (!budget.allowed) return json(res, { ok: false, error: budget.reason, budget }, 409);
7448
7462
  const started = startWork({ initiative: r.initiative, ws: r.ws, agent: r.agent, name: r.details.session_name,
7449
- origin: 'plan-item', initialInput: r.prompt + '\r', planItem: r.item });
7463
+ origin: 'plan-item', initialInput: r.prompt + '\r', planItem: r.item, profile: defaultProfileFor(r.agent) });
7450
7464
  if (!started.ok) return json(res, { ok: false, error: started.error }, 409);
7451
7465
  // The session's token is never projected; the page gets where it runs and the item it is bound to.
7452
7466
  return json(res, { ok: true, session: { id: started.session.id, name: started.session.name, ws: started.session.ws, plan_item: started.session.plan_item || null },