create-pmos 0.67.0 → 0.68.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/guide/command-palette.md +1 -1
- package/workspace/guide/layout.md +36 -9
- package/workspace/lib/sync.js +32 -4
- package/workspace/package-lock.json +1 -1
- package/workspace/package.json +1 -1
- package/workspace/public/pm/components.css +610 -3
- package/workspace/public/pm/doc-editor.js +473 -0
- package/workspace/public/pm/doc-rail.js +161 -0
- package/workspace/public/pm/doc-remote.js +180 -0
- package/workspace/public/pm/dock-sessions.js +57 -13
- package/workspace/public/pm/frame-copy.js +2 -0
- package/workspace/public/pm/icons.js +1 -1
- package/workspace/public/pm/panel-tabs.js +85 -3
- package/workspace/public/pm/primitives.js +8 -4
- package/workspace/public/pm/state.js +3 -0
- package/workspace/public/pm/tab-doc.js +175 -170
- package/workspace/public/vendor/lucide-sprite.js +15 -2
- package/workspace/replaced-tests.json +43 -0
- package/workspace/server.js +30 -0
- package/workspace/test-lists/workspace-tabs-declutter.json +13 -0
- package/workspace/third-party/README.md +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.68.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-07T02:
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-07T02:38:51Z"}
|
|
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-07T02:
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-07T02:38:51Z"}
|
|
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-07T02:
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-07T02:38:51Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -67,7 +67,7 @@ The palette reads the lists of all three scopes when it opens. When a list canno
|
|
|
67
67
|
|
|
68
68
|
The palette opens as a sheet from the bottom edge. Each row is at least 48 pixels high, and the rows are reached by touch.
|
|
69
69
|
|
|
70
|
-
On a phone, the scratchpad is the **compose line** at the bottom of a terminal tab. (On a desktop, open it with
|
|
70
|
+
On a phone, the scratchpad is the **compose line** at the bottom of a terminal tab. (On a desktop, open it with **Scratchpad** in the bar at the bottom of the dock.) Type a message there and press **Send**. The text is typed into the session and the line clears. The line keeps your text for each session until you send it, so a message you did not finish is still there when you come back.
|
|
71
71
|
|
|
72
72
|
## Related
|
|
73
73
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Guide
|
|
3
3
|
title: "Guide: layout"
|
|
4
4
|
description: "The frame: one top bar, the place on screen, the detail and the agent dock; the phone tabs and decks; the keys; and the older layout."
|
|
5
|
-
timestamp: 2026-10-
|
|
5
|
+
timestamp: 2026-10-07
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Guide: layout
|
|
@@ -17,7 +17,7 @@ The top bar has these parts, from left to right:
|
|
|
17
17
|
- **Branches and sessions** is in ⌘K. It opens a sheet with every branch of the repository (main first) and how many sessions run in each. Press a branch to see its sessions, open one, end one, or start a session there. **+ branch** makes a branch, and **+ session** starts one with a branch, an agent and a profile. With All repositories, the sheet lists every repository.
|
|
18
18
|
- **Agenda, Initiatives, Board, Reports, Docs and People** are the six places. Press one to show it. The Agenda count shows how many items need you. Docs shows whether each document is the same as its Google Docs and Confluence copy. People shows the people you work with in the repository ([People](people.md)).
|
|
19
19
|
- **Search or capture ⌘K** opens the command palette. Type to find an initiative, a repository, an open tab or a command.
|
|
20
|
-
- **Sync** shows only when the current repository is not in sync with its remote. Press it to fetch, bring in the work of the other machine, record your changes and push. If the repository has not fetched in the last 10 minutes, Sync also shows, because a push from another machine is not visible until a fetch. When the repository fetched recently and is in sync, Sync is not shown. If Sync stops, the message names the step that stopped.
|
|
20
|
+
- **Sync** shows only when the current repository is not in sync with its remote. Press it to fetch, bring in the work of the other machine, record your changes and push. If the repository has not fetched in the last 10 minutes, Sync also shows, because a push from another machine is not visible until a fetch. When the repository fetched recently and is in sync, Sync is not shown. When two machines add lines to the same ledger (a `.jsonl` file under `pmos/` or `planning/`), Sync keeps the lines from both machines. If Sync stops, the message names the step that stopped. If both machines changed the same lines of another file, the message names the file. Keep the version you want in that file on one machine, sync it, then sync the other machine.
|
|
21
21
|
- **Sync on its own.** If you forget to press Sync, the workspace syncs for you. It syncs each repository once when the workspace starts. It also syncs a repository that has records not yet pushed when no record changed for 5 minutes. In a document repository it syncs every changed file on any branch. In a code repository it syncs only on the default branch (for example `main`), and only when every commit not yet pushed changes records and no code. If you close the tab while records are not yet pushed, the browser asks you first. To turn automatic sync off, start the workspace with `PMOS_AUTO_SYNC=0`.
|
|
22
22
|
- **Settings** opens the settings of the workspace.
|
|
23
23
|
|
|
@@ -48,18 +48,45 @@ The dock holds your terminals, documents, conversations, skills and web pages. I
|
|
|
48
48
|
|
|
49
49
|
- Press ⌘J (Ctrl-J), or the button on the rail, to open the dock. Press ⌘J again, or the fold button in the dock, to fold it to the rail.
|
|
50
50
|
- The dock never opens by itself. A tab that opens while the dock is a rail waits in the dock until you open it.
|
|
51
|
-
- The dock lists the sessions of the repository by initiative. A session is in the group of the initiative it is recorded on, or of its `initiative/<id>` branch. Then come **Not linked** (no initiative) and **Scheduled** (started by a schedule). Then **Open** lists the tabs that are not sessions, for example documents and skills.
|
|
52
|
-
- Each session row
|
|
51
|
+
- The dock lists the sessions of the repository by initiative. A session is in the group of the initiative it is recorded on, or of its `initiative/<id>` branch. Then come **Not linked** (no initiative) and **Scheduled** (started by a schedule). Then **Open** lists the tabs that are not sessions, for example documents and skills. Each one shows its title, what it is (for example PRD, Document or Web page) and its branch when it is not main. **✕** on an Open row closes the tab only. A terminal keeps running.
|
|
52
|
+
- Each session row starts with a mark for its state: ● working, ◼ quiet, ◆ needs you, ◇ maybe blocked, ○ ended. Under the name, the row shows the state when the session is not working, the type of work when one is set, and the branch when it is not main and not the initiative's own branch. Running sessions come first and ended sessions last. Press the row to bring the session to the front.
|
|
53
|
+
- Press the name of a group to fold it or open it. The number beside it counts its sessions. **Scheduled** is folded while none of its runs is running, because each run leaves an ended session. The group of the session in front always stays open, and the workspace keeps your choice for each group.
|
|
54
|
+
- **⋯** and **✕** show on the row in front, on the row under the pointer and on the row with the keyboard focus. On a touch screen they always show.
|
|
53
55
|
- Press **⋯** on a row to change the name, the initiative or the type of work.
|
|
54
56
|
- Press **✕** on a row to end the session. The row asks "End “name”?". Press **End** or **Cancel**. When you end the last running session on its own working copy, the message offers **Remove working copy**. This removes the folder only. The branch stays. If the folder has unsaved work, the workspace keeps it and tells you why.
|
|
55
57
|
- **+ Session** at the top of the dock, or **+** on an initiative, opens one sheet. Type a name, or leave it empty to name the session after the skill and the initiative. **For** is the initiative. **On** is where the session runs. **Automatic** uses the initiative's own working copy on `initiative/<id>` (made the first time), or the main tree when there is no initiative. You can also choose a branch that has a working copy, or **New branch…**. Then press **Blank session** (the CLI and model from Settings) or a skill. The session starts at once.
|
|
56
58
|
- The icons at the bottom of the dock show the sessions as a strip on top, give the dock the whole window (Focus), and fold the dock. On top, a select chooses the group, and each session of the group is a button. **+** starts a session, and **✕** ends the session in front.
|
|
57
59
|
- To make the dock wider or narrower, drag its left edge. With the edge focused, the left and right arrow keys change the width in steps. Double-click the edge to go back to the default width. The width is kept in this browser.
|
|
58
60
|
- **Focus** in the dock gives the dock the whole window. The place and the detail are hidden, and the dock stays full width on every place. Press Focus again, or ⌘⇧J (Ctrl-Shift-J), to show the place again. Folding the dock also ends Focus.
|
|
59
|
-
-
|
|
61
|
+
- **Scratchpad** in the bar at the bottom of the dock opens and closes the scratchpad. The pad keeps one note for each terminal session, and one for the repository when no terminal is in front. **Send to terminal** types the note into the session in front and presses Enter. It is available only when a terminal is in front. **Copy** and **Clear** act on the note.
|
|
62
|
+
- The same bottom bar shows the title of the tab in front and a copy button that names what it copies: **Copy document** for a document, **Copy attach command** for a terminal.
|
|
63
|
+
- A document tab has one bar. The words at its start say whether the file is saved on disk or has unsaved changes. The copy button beside them shows the file's Confluence or Google Docs copy (see [Edit a document in place](#edit-a-document-in-place)). **Changes**, **Revert** and **Save** show when there are unsaved changes. **Markdown** shows the whole file as markdown in one box; press it again to go back. A file that you cannot edit has **Source** in its place. **⋯** holds the rest: the reading width (full width, or lines of 72 characters), **Normalize line breaks** (it joins hard-wrapped paragraphs into one line each in the buffer; read the result in Changes, then save) and **Hide the outline and connections**. **Run scripts** shows on the bar for an HTML page that can run its own scripts.
|
|
60
64
|
|
|
61
65
|
Folding the dock, or going to another place, does not touch your terminals.
|
|
62
66
|
|
|
67
|
+
## Edit a document in place
|
|
68
|
+
|
|
69
|
+
A markdown document is edited where you read it. The layout does not change when you edit.
|
|
70
|
+
|
|
71
|
+
- Press a paragraph, a heading, a list or a table to edit it. That block shows as markdown, with the cursor where you pressed. The other blocks stay as they are.
|
|
72
|
+
- **Esc** finishes the block. **Enter** at the end of a block starts a new block, and continues a list. **Backspace** at the start of a block joins it to the block above. The arrow keys move to the block above or below.
|
|
73
|
+
- **Ctrl** or **⌘** with **B**, **I**, **E** or **K** makes the selection bold, italic, code or a link. The same four buttons show above the block. **Ctrl** or **⌘** with **S** saves.
|
|
74
|
+
- Type `/` in an empty block to make it a heading, a list, a checklist, a quote or code.
|
|
75
|
+
- Press a checklist box to tick it.
|
|
76
|
+
- The frontmatter shows as **Properties**. Change a one-line value in its box. To change any other value, press **Markdown** and edit the file as text.
|
|
77
|
+
- **+ Add a block** at the end of the document adds a paragraph.
|
|
78
|
+
|
|
79
|
+
A block that you did not edit is saved exactly as it was. Nothing is saved until you press **Save**. **Changes** shows what will change, and **Revert** removes all unsaved changes.
|
|
80
|
+
|
|
81
|
+
The rail on the right has two tabs:
|
|
82
|
+
|
|
83
|
+
- **Outline** lists the headings. Press one to go to it.
|
|
84
|
+
- **Connections** shows the files that this document is tied to, as a graph and as a list. A tie is a link, a frontmatter path, the initiative, the anchor or a decision id. Nothing is guessed from words. Press a file to open it. The list also shows the document's copies, and the findings of the last consistency check that name the document.
|
|
85
|
+
|
|
86
|
+
To hide the rail, use **Hide the outline and connections** in the **⋯** menu.
|
|
87
|
+
|
|
88
|
+
The copy button on the bar shows the document's Confluence (or Google Docs) copy, from the same record as the [Docs page](docs.md). It says which side is newer. Press it to see both sides and the first difference. **Use PMOS version** writes the document to the copy, and **Use Confluence version** writes the copy to the document. Each waits 5 seconds, during which you can press **Undo**. **Compare again** reads every copy and changes nothing. A copy never changes by itself. If the document has no copy, the button says **Add a copy**: paste the address of a Confluence page or a Google Doc. Save your edits before you sync, because a sync uses the saved file.
|
|
89
|
+
|
|
63
90
|
## The detail
|
|
64
91
|
|
|
65
92
|
The detail shows one item in full, for example an initiative or the profiles. It opens as a column between the place and the dock when you open an item. Press × at its top, or press Esc, to close it.
|
|
@@ -141,7 +168,7 @@ The `+` tile on the rail opens Add repository as a tab. The tab shows the scan o
|
|
|
141
168
|
|
|
142
169
|
The list has a switch for Initiatives and Files. Choose Files to see the files of the current repository. Type in the filter to find a file. Press a file to open it in a tab.
|
|
143
170
|
|
|
144
|
-
Under a terminal tab, the scratchpad holds a note that you can send to the terminal, copy or clear. The `+` session button opens a chooser for the new session. In a document tab, the
|
|
171
|
+
Under a terminal tab, the scratchpad holds a note that you can send to the terminal, copy or clear. The `+` session button opens a chooser for the new session. In a document tab, the rail on the right shows the document's outline and its connections.
|
|
145
172
|
|
|
146
173
|
The list has a switch for Work, Today and All repos, a filter, and the groups Needs you, In progress and Done. The badge on a row names the step where the initiative is, for example `review`, `accept` or `build`.
|
|
147
174
|
|
|
@@ -171,13 +198,13 @@ The third column holds your terminals, documents, conversations and guide pages.
|
|
|
171
198
|
|
|
172
199
|
Press a file anywhere in the workspace to open it in a tab. Any text file opens, whatever its extension.
|
|
173
200
|
|
|
174
|
-
- **Markdown** opens as a document
|
|
201
|
+
- **Markdown** opens as a document that you edit in place. See [Edit a document in place](#edit-a-document-in-place).
|
|
175
202
|
- **JSON** opens pretty-printed. If the file is not valid JSON, the tab shows the text as written and says why.
|
|
176
203
|
- **JSONL** opens as numbered records, one for each line. The tab marks a line that is not valid JSON.
|
|
177
|
-
- **HTML** opens
|
|
204
|
+
- **HTML** opens as the page it is. Press **Source** to see its source. The page cannot run scripts and cannot load anything from the network. To run the page's own scripts, press `run scripts`. This grant is for this tab only. The page can then run the scripts written inside it, but it still cannot load anything from the network, cannot load a script from another location, and cannot read the workspace. Press `scripts running — stop` to stop the scripts. Closing the tab also removes the grant.
|
|
178
205
|
- **Other text** opens as numbered source lines. Spreadsheets, PDFs and images open in their own views.
|
|
179
206
|
|
|
180
|
-
A tab for a file that is not markdown is read-only. It has no
|
|
207
|
+
A tab for a file that is not markdown is read-only. It has no Markdown, Changes or save control, because the workspace saves only markdown.
|
|
181
208
|
|
|
182
209
|
If a file cannot be shown, the tab says why. The reasons are: the file is binary, it is larger than 2 MB, it is not valid text, or it is outside the workspace.
|
|
183
210
|
|
package/workspace/lib/sync.js
CHANGED
|
@@ -54,6 +54,26 @@ const isRecord = (rel) => RECORD_PATHS.some((p) => rel === p || rel.startsWith(`
|
|
|
54
54
|
// PM repositories append ledgers from several machines; `merge=union` keeps both sides' lines instead
|
|
55
55
|
// of conflicting (D70). Added once, committed with the records. Never touches a product-code repository.
|
|
56
56
|
const UNION_LINE = '*.jsonl merge=union';
|
|
57
|
+
|
|
58
|
+
// The same rule, given to the replay itself rather than read from the repository. A committed `.gitattributes` holds
|
|
59
|
+
// only for a PM-profile repository, and only once it is already on the remote: a rebase reads the attributes of the
|
|
60
|
+
// tree it replays ONTO, so a union line arriving in the very commit being replayed does not apply to it. A code
|
|
61
|
+
// repository never carries the line at all, so two machines appending pmos/state/runs.jsonl stopped Sync at
|
|
62
|
+
// integrate every time. `core.attributesFile` is the lowest-precedence attribute source, so a repository's own
|
|
63
|
+
// `.gitattributes` still wins; it is set for this one rebase and written to no tracked file.
|
|
64
|
+
// A code repository unions its record ledgers only; a document repository every ledger.
|
|
65
|
+
function unionAttributesFor(root) {
|
|
66
|
+
const lines = profileOf(root) === 'pm-artifacts' ? [UNION_LINE]
|
|
67
|
+
: RECORD_PATHS.filter((p) => !p.startsWith('.')).map((p) => `${p}/**/*.jsonl merge=union`);
|
|
68
|
+
const f = path.join(os.tmpdir(), `pmos-sync-attributes-${profileOf(root)}`);
|
|
69
|
+
const text = `${lines.join('\n')}\n`;
|
|
70
|
+
try {
|
|
71
|
+
// written only when it differs, so a second repository's sync never reads it half-written
|
|
72
|
+
if ((fs.existsSync(f) ? fs.readFileSync(f, 'utf8') : '') !== text) fs.writeFileSync(f, text);
|
|
73
|
+
return f;
|
|
74
|
+
} catch { return null; }
|
|
75
|
+
}
|
|
76
|
+
|
|
57
77
|
function ensureUnionAttribute(root) {
|
|
58
78
|
try {
|
|
59
79
|
const prof = fs.readFileSync(path.join(root, '.pmos-profile'), 'utf8').split('\n').map((l) => l.replace(/#.*$/, '').trim()).find(Boolean);
|
|
@@ -136,11 +156,19 @@ async function sync(root, { label } = {}) {
|
|
|
136
156
|
const s2 = await status(root);
|
|
137
157
|
if (s2.behind) {
|
|
138
158
|
// Fast-forward when we have nothing of our own; otherwise replay OUR unpushed commits on top.
|
|
139
|
-
const
|
|
159
|
+
const attrs = s2.ahead ? unionAttributesFor(root) : null;
|
|
160
|
+
const verb = s2.ahead ? [...(attrs ? ['-c', `core.attributesFile=${attrs}`] : []), 'rebase', '--autostash', s2.upstream]
|
|
161
|
+
: ['merge', '--ff-only', s2.upstream];
|
|
140
162
|
const r = await run(root, verb);
|
|
141
163
|
if (!r.ok) {
|
|
142
|
-
if (s2.ahead)
|
|
143
|
-
|
|
164
|
+
if (!s2.ahead) return stop('integrate', `cannot fast-forward — ${r.err.split('\n')[0] || 'see git'}`);
|
|
165
|
+
// Name the files before the abort clears them: the PM has to open these, and "the same lines" alone says not where.
|
|
166
|
+
const u = await run(root, ['diff', '--name-only', '--diff-filter=U']);
|
|
167
|
+
const files = u.ok && u.out ? u.out.split('\n').filter(Boolean) : [];
|
|
168
|
+
await run(root, ['rebase', '--abort']);
|
|
169
|
+
return stop('integrate', files.length
|
|
170
|
+
? `both machines changed ${files.join(', ')} — rebase aborted, nothing changed; keep the version you want in each file on one machine, sync it, then sync the other`
|
|
171
|
+
: `the two machines changed the same lines — rebase aborted, nothing changed — ${r.err.split('\n')[0] || 'see git'}`);
|
|
144
172
|
}
|
|
145
173
|
done('integrate', s2.ahead ? `replayed ${s2.ahead} local commit(s) on the remote` : `fast-forwarded ${s2.behind} commit(s)`);
|
|
146
174
|
} else done('integrate', 'nothing to bring in');
|
|
@@ -183,5 +211,5 @@ async function defaultBranchOf(root, remote) {
|
|
|
183
211
|
return null;
|
|
184
212
|
}
|
|
185
213
|
|
|
186
|
-
module.exports = { status, sync, ensureUnionAttribute, RECORD_PATHS, isRecord, REMOTE_FRESH_MS,
|
|
214
|
+
module.exports = { status, sync, ensureUnionAttribute, unionAttributesFor, RECORD_PATHS, isRecord, REMOTE_FRESH_MS,
|
|
187
215
|
unpushedPaths, newestDirtyRecordMs, defaultBranchOf, profileOf };
|
package/workspace/package.json
CHANGED