create-pmos 0.80.0 → 0.81.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/scripts/workflow-state.js +2 -2
- package/payload/state/discoveries.md +1 -1
- package/workspace/guide/layout.md +3 -1
- package/workspace/lib/engine/workflow-state.js +2 -2
- package/workspace/package-lock.json +2 -2
- package/workspace/package.json +1 -1
- package/workspace/public/pm/app.js +3 -1
- package/workspace/public/pm/components.css +61 -56
- package/workspace/public/pm/dest-today.js +3 -2
- package/workspace/public/pm/dom.js +60 -0
- package/workspace/public/pm/roadmap.js +2 -2
- package/workspace/public/pm/topbar.js +4 -1
- package/workspace/run-tests.js +5 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.81.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-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-07T14:50:35Z"}
|
|
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-07T14:50:35Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -1104,8 +1104,8 @@ function computeWorkflowDerived(id, opts) {
|
|
|
1104
1104
|
steps.push(step('accept', 'accept', 'blocked', `blocked by: ${blockers.join(', ')}`,
|
|
1105
1105
|
'the Acceptance Gate is the PM\'s (D12) — but it does not open over an unfinished ladder'));
|
|
1106
1106
|
} else {
|
|
1107
|
-
steps.push(step('accept', 'accept', 'pending', '
|
|
1108
|
-
'
|
|
1107
|
+
steps.push(step('accept', 'accept', 'pending', 'no acceptance record needed (D94)',
|
|
1108
|
+
'merge once the required checks are green and the PM has not returned the run'));
|
|
1109
1109
|
}
|
|
1110
1110
|
}
|
|
1111
1111
|
|
|
@@ -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-07T14:50:35Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -11,11 +11,13 @@ On a desktop screen the workspace is one frame. It has one top bar, the place on
|
|
|
11
11
|
|
|
12
12
|
## The top bar
|
|
13
13
|
|
|
14
|
+
Corners are rounded a little: 6 pixels on buttons and fields, 8 pixels on cards and sheets. Buttons and tabs use normal capital letters. Only the small headings above a list stay in capitals. The page does not scroll. Only the place on screen scrolls, and the top bar stays where it is.
|
|
15
|
+
|
|
14
16
|
The top bar has these parts, from left to right:
|
|
15
17
|
|
|
16
18
|
- **Repository ▾** names the current repository. Press it to choose another repository, or **All repositories** to see the work of every repository at once. All repositories replaces the old Portfolio mode.
|
|
17
19
|
- **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
|
-
- **Agenda, Initiatives, Board, Reports, Docs and People** are the
|
|
20
|
+
- **Agenda, My work, Initiatives, Board, Reports, Docs and People** are the places. Each has an icon before its name, the same icon as on the phone. 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
21
|
- **Search or capture ⌘K** opens the command palette. Type to find an initiative, a repository, an open tab or a command.
|
|
20
22
|
- **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, Sync changes nothing and names the files. Then choose **Push this machine’s version** or **Pull the other machine’s version**. Sync keeps that version of those files, keeps all other changes from both machines, and finishes. It never overwrites the remote history.
|
|
21
23
|
- **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`.
|
|
@@ -1104,8 +1104,8 @@ function computeWorkflowDerived(id, opts) {
|
|
|
1104
1104
|
steps.push(step('accept', 'accept', 'blocked', `blocked by: ${blockers.join(', ')}`,
|
|
1105
1105
|
'the Acceptance Gate is the PM\'s (D12) — but it does not open over an unfinished ladder'));
|
|
1106
1106
|
} else {
|
|
1107
|
-
steps.push(step('accept', 'accept', 'pending', '
|
|
1108
|
-
'
|
|
1107
|
+
steps.push(step('accept', 'accept', 'pending', 'no acceptance record needed (D94)',
|
|
1108
|
+
'merge once the required checks are green and the PM has not returned the run'));
|
|
1109
1109
|
}
|
|
1110
1110
|
}
|
|
1111
1111
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmos-workspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.81.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.81.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@xterm/addon-fit": "^0.11.0",
|
package/workspace/package.json
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* the shell still comes up.
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
|
-
import { el, clear, setChildren } from './dom.js';
|
|
33
|
+
import { el, clear, setChildren, keepScroll } from './dom.js';
|
|
34
34
|
import { state, setState, subscribe, flush, FIXTURE, LEGACY_LAYOUT_FLAG } from './state.js';
|
|
35
35
|
/* The phone surface (≤760px). It owns its own chrome and decides which single panel is visible;
|
|
36
36
|
* the shell's only part in it is to keep all four hosts mounted below the breakpoint and to hand
|
|
@@ -563,6 +563,8 @@ let frDetail = null;
|
|
|
563
563
|
const hostMain = el('div', { class: 'fr-main', 'data-fr': 'main' });
|
|
564
564
|
const destHosts = {};
|
|
565
565
|
for (const id of FRAME_DESTS) destHosts[id] = el('div', { class: 'fr-dest', 'data-fr-host': id, hidden: true });
|
|
566
|
+
// each place keeps its reading position when it redraws through a short state (dom.js keepScroll)
|
|
567
|
+
for (const id of FRAME_DESTS) keepScroll(destHosts[id]);
|
|
566
568
|
const frEnd = el('span', { class: 'fr-end', 'aria-hidden': 'true' });
|
|
567
569
|
|
|
568
570
|
function setupFrame() {
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* primitives `.pmc-*` — written only by primitives.js and icons.js (R28-9)
|
|
6
6
|
* layouts `.bd-*` — the Breakdown's own arrangement of those primitives
|
|
7
7
|
* Every selector starts with `.pmc-` or `.bd-`. Colours are tokens.css variables only; there is no hex and no oklch here.
|
|
8
|
-
* Radius
|
|
8
|
+
* Radius (R27 as amended by the PM 2026-10-07): 6px on controls (buttons, inputs, selects, tabs, segmented controls,
|
|
9
|
+
* badges, menu items, checkboxes), 8px on cards, sheets, dialogs, menus, tiles and panels; a dot (50% or a pill) stays round.
|
|
10
|
+
* Buttons, tabs and segmented controls use sentence case; small non-interactive eyebrows may stay uppercase at 0.04em. Hover rules sit inside (hover: hover). Under a coarse pointer every
|
|
9
11
|
* control is at least 44px and every row and card at least 48px.
|
|
10
12
|
*/
|
|
11
13
|
|
|
@@ -53,15 +55,13 @@
|
|
|
53
55
|
min-height: 32px;
|
|
54
56
|
padding: 0 14px;
|
|
55
57
|
border: 1px solid var(--border);
|
|
56
|
-
border-radius:
|
|
58
|
+
border-radius: 6px;
|
|
57
59
|
background: transparent;
|
|
58
60
|
color: var(--foreground);
|
|
59
61
|
font-family: var(--sans);
|
|
60
62
|
font-size: 12px;
|
|
61
63
|
line-height: 1;
|
|
62
|
-
font-weight:
|
|
63
|
-
letter-spacing: 0.1em;
|
|
64
|
-
text-transform: uppercase;
|
|
64
|
+
font-weight: 500;
|
|
65
65
|
white-space: nowrap;
|
|
66
66
|
cursor: pointer;
|
|
67
67
|
user-select: none;
|
|
@@ -128,6 +128,7 @@
|
|
|
128
128
|
display: none;
|
|
129
129
|
}
|
|
130
130
|
.pmc-field {
|
|
131
|
+
position: relative;
|
|
131
132
|
display: flex;
|
|
132
133
|
flex-direction: column;
|
|
133
134
|
gap: 2px;
|
|
@@ -141,6 +142,8 @@
|
|
|
141
142
|
}
|
|
142
143
|
.pmc-field__label--hidden {
|
|
143
144
|
position: absolute;
|
|
145
|
+
top: 0;
|
|
146
|
+
left: 0;
|
|
144
147
|
width: 1px;
|
|
145
148
|
height: 1px;
|
|
146
149
|
overflow: hidden;
|
|
@@ -158,7 +161,7 @@
|
|
|
158
161
|
padding: 0;
|
|
159
162
|
border: 0;
|
|
160
163
|
border-bottom: 1px solid var(--input);
|
|
161
|
-
border-radius:
|
|
164
|
+
border-radius: 6px;
|
|
162
165
|
background: transparent;
|
|
163
166
|
color: var(--foreground);
|
|
164
167
|
font-family: var(--sans);
|
|
@@ -218,7 +221,7 @@
|
|
|
218
221
|
height: 16px;
|
|
219
222
|
margin: 0;
|
|
220
223
|
border: 1px solid var(--muted-foreground);
|
|
221
|
-
border-radius:
|
|
224
|
+
border-radius: 6px;
|
|
222
225
|
background: transparent;
|
|
223
226
|
cursor: pointer;
|
|
224
227
|
}
|
|
@@ -249,7 +252,7 @@
|
|
|
249
252
|
font-size: 11px;
|
|
250
253
|
line-height: 16px;
|
|
251
254
|
font-weight: 600;
|
|
252
|
-
letter-spacing: 0.
|
|
255
|
+
letter-spacing: 0.04em;
|
|
253
256
|
text-transform: uppercase;
|
|
254
257
|
white-space: nowrap;
|
|
255
258
|
color: var(--muted-foreground);
|
|
@@ -452,7 +455,7 @@
|
|
|
452
455
|
min-width: 0;
|
|
453
456
|
min-height: 36px;
|
|
454
457
|
padding: 10px 12px;
|
|
455
|
-
border-radius:
|
|
458
|
+
border-radius: 8px;
|
|
456
459
|
background: var(--card);
|
|
457
460
|
color: var(--foreground);
|
|
458
461
|
box-shadow: 0 0 0 1px color-mix(in oklab, var(--foreground) 9%, transparent),
|
|
@@ -553,14 +556,14 @@
|
|
|
553
556
|
min-height: 26px;
|
|
554
557
|
padding: 0 12px;
|
|
555
558
|
border: 1px solid transparent;
|
|
556
|
-
border-radius:
|
|
559
|
+
border-radius: 6px;
|
|
557
560
|
background: transparent;
|
|
558
561
|
color: color-mix(in oklab, var(--foreground) 68%, transparent);
|
|
559
562
|
font-family: var(--sans);
|
|
560
563
|
font-size: 12px;
|
|
561
564
|
line-height: 1;
|
|
562
565
|
font-weight: 600;
|
|
563
|
-
letter-spacing: 0.
|
|
566
|
+
letter-spacing: 0.04em;
|
|
564
567
|
text-transform: uppercase;
|
|
565
568
|
white-space: nowrap;
|
|
566
569
|
cursor: pointer;
|
|
@@ -615,7 +618,7 @@
|
|
|
615
618
|
overflow: auto;
|
|
616
619
|
padding: 4px;
|
|
617
620
|
border: 0;
|
|
618
|
-
border-radius:
|
|
621
|
+
border-radius: 8px;
|
|
619
622
|
background: var(--card);
|
|
620
623
|
color: var(--foreground);
|
|
621
624
|
font-family: var(--sans);
|
|
@@ -633,7 +636,7 @@
|
|
|
633
636
|
min-height: 32px;
|
|
634
637
|
padding: 0 12px;
|
|
635
638
|
border: 0;
|
|
636
|
-
border-radius:
|
|
639
|
+
border-radius: 6px;
|
|
637
640
|
background: transparent;
|
|
638
641
|
color: var(--foreground);
|
|
639
642
|
font: inherit;
|
|
@@ -663,7 +666,7 @@
|
|
|
663
666
|
margin: 0;
|
|
664
667
|
padding: 0;
|
|
665
668
|
border: 0;
|
|
666
|
-
border-radius:
|
|
669
|
+
border-radius: 8px;
|
|
667
670
|
background: var(--card);
|
|
668
671
|
color: var(--foreground);
|
|
669
672
|
font-family: var(--sans);
|
|
@@ -794,7 +797,7 @@
|
|
|
794
797
|
min-height: 44px;
|
|
795
798
|
padding: 4px 4px 4px 16px;
|
|
796
799
|
border: 0;
|
|
797
|
-
border-radius:
|
|
800
|
+
border-radius: 8px;
|
|
798
801
|
background: var(--card);
|
|
799
802
|
color: var(--foreground);
|
|
800
803
|
font-family: var(--sans);
|
|
@@ -936,7 +939,7 @@
|
|
|
936
939
|
margin: 0;
|
|
937
940
|
padding: 0;
|
|
938
941
|
border: 0;
|
|
939
|
-
border-radius:
|
|
942
|
+
border-radius: 6px;
|
|
940
943
|
background: transparent;
|
|
941
944
|
color: inherit;
|
|
942
945
|
transform: translateY(-50%);
|
|
@@ -1084,7 +1087,7 @@
|
|
|
1084
1087
|
margin-right: 10px;
|
|
1085
1088
|
font-size: 12px;
|
|
1086
1089
|
font-weight: 600;
|
|
1087
|
-
letter-spacing: 0.
|
|
1090
|
+
letter-spacing: 0.04em;
|
|
1088
1091
|
text-transform: uppercase;
|
|
1089
1092
|
}
|
|
1090
1093
|
.bd-entry__meta {
|
|
@@ -1200,7 +1203,7 @@
|
|
|
1200
1203
|
font-size: 11px;
|
|
1201
1204
|
line-height: 16px;
|
|
1202
1205
|
font-weight: 600;
|
|
1203
|
-
letter-spacing: 0.
|
|
1206
|
+
letter-spacing: 0.04em;
|
|
1204
1207
|
text-transform: uppercase;
|
|
1205
1208
|
color: var(--muted-foreground);
|
|
1206
1209
|
}
|
|
@@ -1287,7 +1290,7 @@
|
|
|
1287
1290
|
font-size: 12px;
|
|
1288
1291
|
line-height: 16px;
|
|
1289
1292
|
font-weight: 600;
|
|
1290
|
-
letter-spacing: 0.
|
|
1293
|
+
letter-spacing: 0.04em;
|
|
1291
1294
|
text-transform: uppercase;
|
|
1292
1295
|
}
|
|
1293
1296
|
.bd-col__n {
|
|
@@ -1545,10 +1548,9 @@
|
|
|
1545
1548
|
.in-head__title {
|
|
1546
1549
|
flex: 1 1 auto;
|
|
1547
1550
|
margin: 0;
|
|
1548
|
-
font-size:
|
|
1549
|
-
line-height:
|
|
1550
|
-
font-weight:
|
|
1551
|
-
letter-spacing: -0.01em;
|
|
1551
|
+
font-size: 20px;
|
|
1552
|
+
line-height: 28px;
|
|
1553
|
+
font-weight: 500;
|
|
1552
1554
|
}
|
|
1553
1555
|
.in-body {
|
|
1554
1556
|
display: flex;
|
|
@@ -1645,7 +1647,7 @@
|
|
|
1645
1647
|
font-size: 12px;
|
|
1646
1648
|
line-height: 16px;
|
|
1647
1649
|
font-weight: 600;
|
|
1648
|
-
letter-spacing: 0.
|
|
1650
|
+
letter-spacing: 0.04em;
|
|
1649
1651
|
text-transform: uppercase;
|
|
1650
1652
|
font-variant-numeric: tabular-nums;
|
|
1651
1653
|
}
|
|
@@ -1670,7 +1672,7 @@
|
|
|
1670
1672
|
font-size: 12px;
|
|
1671
1673
|
line-height: 16px;
|
|
1672
1674
|
font-weight: 600;
|
|
1673
|
-
letter-spacing: 0.
|
|
1675
|
+
letter-spacing: 0.04em;
|
|
1674
1676
|
text-transform: uppercase;
|
|
1675
1677
|
}
|
|
1676
1678
|
.in-col__count {
|
|
@@ -1910,7 +1912,7 @@
|
|
|
1910
1912
|
font-size: 12px;
|
|
1911
1913
|
line-height: 16px;
|
|
1912
1914
|
font-weight: 600;
|
|
1913
|
-
letter-spacing: 0.
|
|
1915
|
+
letter-spacing: 0.04em;
|
|
1914
1916
|
text-transform: uppercase;
|
|
1915
1917
|
}
|
|
1916
1918
|
.in-starters {
|
|
@@ -2133,7 +2135,7 @@
|
|
|
2133
2135
|
margin: 0;
|
|
2134
2136
|
font-size: 12px;
|
|
2135
2137
|
font-weight: 600;
|
|
2136
|
-
letter-spacing: 0.
|
|
2138
|
+
letter-spacing: 0.04em;
|
|
2137
2139
|
text-transform: uppercase;
|
|
2138
2140
|
color: var(--muted-foreground);
|
|
2139
2141
|
}
|
|
@@ -2266,6 +2268,8 @@
|
|
|
2266
2268
|
min-width: 0;
|
|
2267
2269
|
min-height: 0;
|
|
2268
2270
|
overflow-y: auto;
|
|
2271
|
+
/* the destination is the one thing that scrolls: a wheel at its end does not move the page and its bars */
|
|
2272
|
+
overscroll-behavior: contain;
|
|
2269
2273
|
}
|
|
2270
2274
|
.fr-dest[hidden] {
|
|
2271
2275
|
display: none;
|
|
@@ -2476,7 +2480,7 @@
|
|
|
2476
2480
|
color: var(--muted-foreground);
|
|
2477
2481
|
font-size: 11px;
|
|
2478
2482
|
font-weight: 600;
|
|
2479
|
-
letter-spacing: 0.
|
|
2483
|
+
letter-spacing: 0.04em;
|
|
2480
2484
|
text-transform: uppercase;
|
|
2481
2485
|
text-overflow: ellipsis;
|
|
2482
2486
|
white-space: nowrap;
|
|
@@ -2765,7 +2769,7 @@
|
|
|
2765
2769
|
color: var(--muted-foreground);
|
|
2766
2770
|
font-size: 11px;
|
|
2767
2771
|
font-weight: 600;
|
|
2768
|
-
letter-spacing: 0.
|
|
2772
|
+
letter-spacing: 0.04em;
|
|
2769
2773
|
text-transform: uppercase;
|
|
2770
2774
|
list-style: none;
|
|
2771
2775
|
cursor: pointer;
|
|
@@ -3007,7 +3011,7 @@
|
|
|
3007
3011
|
color: var(--muted-foreground);
|
|
3008
3012
|
font-size: 11px;
|
|
3009
3013
|
font-weight: 600;
|
|
3010
|
-
letter-spacing: 0.
|
|
3014
|
+
letter-spacing: 0.04em;
|
|
3011
3015
|
text-transform: uppercase;
|
|
3012
3016
|
}
|
|
3013
3017
|
.fr-rail__graph {
|
|
@@ -3089,7 +3093,7 @@
|
|
|
3089
3093
|
color: var(--muted-foreground);
|
|
3090
3094
|
font-size: 11px;
|
|
3091
3095
|
font-weight: 600;
|
|
3092
|
-
letter-spacing: 0.
|
|
3096
|
+
letter-spacing: 0.04em;
|
|
3093
3097
|
text-transform: uppercase;
|
|
3094
3098
|
}
|
|
3095
3099
|
.fr-rail__finding[data-status="stale"] .fr-rail__finding-s,
|
|
@@ -3245,7 +3249,7 @@
|
|
|
3245
3249
|
.td-day__warn { color: var(--warning); }
|
|
3246
3250
|
.td-day__mono { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 12px; }
|
|
3247
3251
|
.td-day__steps { display: flex; flex-wrap: wrap; gap: 2px; list-style: none; margin: 0; padding: 0; }
|
|
3248
|
-
.td-day__ts { gap: 6px; padding: 0 6px; letter-spacing: 0.04em; color: var(--muted-foreground); }
|
|
3252
|
+
.td-day__ts { border-radius: 6px; gap: 6px; padding: 0 6px; letter-spacing: 0.04em; color: var(--muted-foreground); }
|
|
3249
3253
|
.td-day__ts[data-step-state="done"], .td-day__ts[data-step-state="now"] { color: var(--foreground); }
|
|
3250
3254
|
.td-day__ts[data-step-state="now"] { background: var(--muted); }
|
|
3251
3255
|
.td-day__ts[aria-current="true"] { border-color: var(--ring); }
|
|
@@ -3260,14 +3264,15 @@
|
|
|
3260
3264
|
.td-day__run--failed { color: var(--destructive); }
|
|
3261
3265
|
.td-day__note { margin: 0; font-size: 12px; line-height: 16px; color: var(--muted-foreground); }
|
|
3262
3266
|
|
|
3263
|
-
.td-day__cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); gap: 12px; align-items:
|
|
3264
|
-
.td-day__left { display: grid; gap: 12px; min-width: 0; }
|
|
3267
|
+
.td-day__cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); gap: 12px; align-items: stretch; }
|
|
3268
|
+
.td-day__left { display: grid; gap: 12px; min-width: 0; align-content: start; }
|
|
3265
3269
|
.td-day__panel, .td-day__detail {
|
|
3266
3270
|
display: grid;
|
|
3267
3271
|
gap: 8px;
|
|
3268
3272
|
min-width: 0;
|
|
3269
3273
|
padding: 10px 12px;
|
|
3270
3274
|
border: 1px solid var(--border);
|
|
3275
|
+
border-radius: 8px;
|
|
3271
3276
|
background: var(--card);
|
|
3272
3277
|
}
|
|
3273
3278
|
.td-day__detail { position: sticky; top: 8px; max-height: calc(100vh - 16px); overflow: auto; align-content: start; }
|
|
@@ -3282,7 +3287,7 @@
|
|
|
3282
3287
|
margin: 0;
|
|
3283
3288
|
font-size: 12px;
|
|
3284
3289
|
font-weight: 600;
|
|
3285
|
-
letter-spacing: 0.
|
|
3290
|
+
letter-spacing: 0.04em;
|
|
3286
3291
|
text-transform: uppercase;
|
|
3287
3292
|
color: var(--muted-foreground);
|
|
3288
3293
|
}
|
|
@@ -3295,7 +3300,7 @@
|
|
|
3295
3300
|
.td-day__lead { min-width: 44px; font: 12px var(--mono); color: var(--muted-foreground); font-variant-numeric: tabular-nums; }
|
|
3296
3301
|
.td-day__chips { display: inline-flex; gap: 4px; }
|
|
3297
3302
|
.td-day__chips { align-items: center; flex-wrap: wrap; justify-content: flex-end; }
|
|
3298
|
-
.td-day__repo { font: inherit; font-size: 12px; color: var(--foreground); background: var(--background); border: 1px solid var(--border); border-radius:
|
|
3303
|
+
.td-day__repo { font: inherit; font-size: 12px; color: var(--foreground); background: var(--background); border: 1px solid var(--border); border-radius: 6px; padding: 2px 6px; max-width: 160px; }
|
|
3299
3304
|
.td-day__repo:focus-visible { outline: 2px solid color-mix(in oklab, var(--ring) 50%, var(--foreground)); outline-offset: 2px; }
|
|
3300
3305
|
.td-day__nowrow { display: flex; align-items: center; gap: 8px; padding: 0 8px; font: 11px var(--mono); color: var(--destructive); }
|
|
3301
3306
|
.td-day__nowrow::after { content: ""; flex: 1; border-top: 2px solid var(--destructive); }
|
|
@@ -3306,7 +3311,7 @@
|
|
|
3306
3311
|
|
|
3307
3312
|
/* the detail */
|
|
3308
3313
|
.td-day__dhead { display: grid; gap: 4px; }
|
|
3309
|
-
.td-day__kind { font: 11px var(--mono); letter-spacing: 0.
|
|
3314
|
+
.td-day__kind { font: 11px var(--mono); letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted-foreground); }
|
|
3310
3315
|
.td-day__dhead h3 { margin: 0; font-size: 16px; line-height: 24px; color: var(--foreground); }
|
|
3311
3316
|
.td-day__detail p { margin: 0; font-size: 14px; line-height: 20px; color: var(--foreground); }
|
|
3312
3317
|
.td-day__detail p.td-day__note { font-size: 12px; line-height: 16px; color: var(--muted-foreground); }
|
|
@@ -3317,11 +3322,10 @@
|
|
|
3317
3322
|
height: 28px;
|
|
3318
3323
|
padding: 0 10px;
|
|
3319
3324
|
border: 1px solid var(--border);
|
|
3325
|
+
border-radius: 6px;
|
|
3320
3326
|
color: var(--foreground);
|
|
3321
3327
|
font-size: 12px;
|
|
3322
|
-
font-weight:
|
|
3323
|
-
letter-spacing: 0.1em;
|
|
3324
|
-
text-transform: uppercase;
|
|
3328
|
+
font-weight: 500;
|
|
3325
3329
|
text-decoration: none;
|
|
3326
3330
|
white-space: nowrap;
|
|
3327
3331
|
}
|
|
@@ -3329,7 +3333,7 @@
|
|
|
3329
3333
|
@media (pointer: coarse) { .td-day__a { min-height: 44px; } }
|
|
3330
3334
|
.td-day__phase, .td-day__rel { display: grid; gap: 4px; }
|
|
3331
3335
|
.td-day__phase h4, .td-day__rel h4, .td-day__notes h4, .td-day__draftbox h4, .td-day__more h4 {
|
|
3332
|
-
margin: 0; font-size: 11px; letter-spacing: 0.
|
|
3336
|
+
margin: 0; font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted-foreground);
|
|
3333
3337
|
}
|
|
3334
3338
|
.td-day__chk { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; font-size: 14px; color: var(--foreground); }
|
|
3335
3339
|
.td-day__chk li { display: grid; grid-template-columns: 16px minmax(0, 1fr); gap: 6px; }
|
|
@@ -3351,7 +3355,7 @@
|
|
|
3351
3355
|
.td-day__draft { margin: 0; max-height: 220px; overflow: auto; padding: 8px 10px; white-space: pre-wrap; font: 12px/1.5 var(--mono); background: var(--muted); color: var(--foreground); }
|
|
3352
3356
|
.td-day__revise { display: grid; gap: 6px; }
|
|
3353
3357
|
/* act and prepare (initiative today-act-prepare): a verb first, one sentence, the steps, one button that fits, the marks */
|
|
3354
|
-
.td-day__verb { display: inline-block; min-width: 58px; padding: 2px 5px; border: 1px solid currentColor; font: 600 10px/1.3 var(--mono); letter-spacing: 0.
|
|
3358
|
+
.td-day__verb { display: inline-block; min-width: 58px; padding: 2px 5px; border: 1px solid currentColor; font: 600 10px/1.3 var(--mono); letter-spacing: 0.04em; text-transform: uppercase; text-align: center; color: var(--foreground); white-space: nowrap; }
|
|
3355
3359
|
.td-day__verb[data-verb="decide"], .td-day__verb[data-verb="prepare"] { color: var(--warning); }
|
|
3356
3360
|
.td-day__acth { margin: 0; font-size: 16px; font-weight: 600; line-height: 1.3; text-wrap: balance; }
|
|
3357
3361
|
.td-day__todo { margin: 0; padding: 7px 10px; border-left: 3px solid var(--foreground); background: var(--muted); font-size: 14px; line-height: 1.45; color: var(--foreground); }
|
|
@@ -3459,7 +3463,7 @@
|
|
|
3459
3463
|
gap: 8px;
|
|
3460
3464
|
min-width: 0;
|
|
3461
3465
|
padding: 10px 12px;
|
|
3462
|
-
border-radius:
|
|
3466
|
+
border-radius: 8px;
|
|
3463
3467
|
background: var(--card);
|
|
3464
3468
|
color: var(--foreground);
|
|
3465
3469
|
box-shadow: 0 0 0 1px var(--border);
|
|
@@ -3553,7 +3557,7 @@
|
|
|
3553
3557
|
}
|
|
3554
3558
|
.td-health {
|
|
3555
3559
|
padding: 8px 12px;
|
|
3556
|
-
border-radius:
|
|
3560
|
+
border-radius: 8px;
|
|
3557
3561
|
background: var(--muted);
|
|
3558
3562
|
box-shadow: inset 2px 0 0 var(--warning);
|
|
3559
3563
|
font-size: 12px;
|
|
@@ -4011,13 +4015,13 @@
|
|
|
4011
4015
|
margin: 16px 16px 0;
|
|
4012
4016
|
padding: 12px;
|
|
4013
4017
|
border: 1px solid var(--border);
|
|
4014
|
-
border-radius:
|
|
4018
|
+
border-radius: 8px;
|
|
4015
4019
|
}
|
|
4016
4020
|
.bo-releases__head {
|
|
4017
4021
|
font-size: 12px;
|
|
4018
4022
|
line-height: 16px;
|
|
4019
4023
|
font-weight: 600;
|
|
4020
|
-
letter-spacing: 0.
|
|
4024
|
+
letter-spacing: 0.04em;
|
|
4021
4025
|
text-transform: uppercase;
|
|
4022
4026
|
}
|
|
4023
4027
|
.bo-releases__note {
|
|
@@ -4036,7 +4040,7 @@
|
|
|
4036
4040
|
.bo-rel--today {
|
|
4037
4041
|
padding: 10px 12px;
|
|
4038
4042
|
border: 1px solid var(--border);
|
|
4039
|
-
border-radius:
|
|
4043
|
+
border-radius: 8px;
|
|
4040
4044
|
background: var(--card);
|
|
4041
4045
|
}
|
|
4042
4046
|
.bo-rel__main {
|
|
@@ -4119,7 +4123,7 @@
|
|
|
4119
4123
|
font-size: 11px;
|
|
4120
4124
|
line-height: 16px;
|
|
4121
4125
|
font-weight: 500;
|
|
4122
|
-
letter-spacing: 0.
|
|
4126
|
+
letter-spacing: 0.04em;
|
|
4123
4127
|
text-transform: uppercase;
|
|
4124
4128
|
color: var(--muted-foreground);
|
|
4125
4129
|
}
|
|
@@ -4266,7 +4270,7 @@
|
|
|
4266
4270
|
font-family: var(--mono);
|
|
4267
4271
|
font-size: 11px;
|
|
4268
4272
|
font-weight: 500;
|
|
4269
|
-
letter-spacing: 0.
|
|
4273
|
+
letter-spacing: 0.04em;
|
|
4270
4274
|
text-transform: uppercase;
|
|
4271
4275
|
color: var(--muted-foreground);
|
|
4272
4276
|
}
|
|
@@ -5883,6 +5887,8 @@
|
|
|
5883
5887
|
}
|
|
5884
5888
|
.nt-sr {
|
|
5885
5889
|
position: absolute;
|
|
5890
|
+
top: 0;
|
|
5891
|
+
left: 0;
|
|
5886
5892
|
width: 1px;
|
|
5887
5893
|
height: 1px;
|
|
5888
5894
|
overflow: hidden;
|
|
@@ -5925,7 +5931,7 @@
|
|
|
5925
5931
|
margin: 0;
|
|
5926
5932
|
font-size: 11px;
|
|
5927
5933
|
font-weight: 500;
|
|
5928
|
-
letter-spacing: .
|
|
5934
|
+
letter-spacing: 0.04em;
|
|
5929
5935
|
text-transform: uppercase;
|
|
5930
5936
|
color: var(--muted-foreground);
|
|
5931
5937
|
}
|
|
@@ -6200,7 +6206,7 @@
|
|
|
6200
6206
|
gap: 2px;
|
|
6201
6207
|
padding: 8px 12px;
|
|
6202
6208
|
border: 1px solid var(--border);
|
|
6203
|
-
border-radius:
|
|
6209
|
+
border-radius: 8px;
|
|
6204
6210
|
background: var(--background);
|
|
6205
6211
|
color: var(--foreground);
|
|
6206
6212
|
font: inherit;
|
|
@@ -6585,10 +6591,9 @@
|
|
|
6585
6591
|
color: var(--foreground);
|
|
6586
6592
|
}
|
|
6587
6593
|
|
|
6588
|
-
/* R27
|
|
6589
|
-
|
|
6590
|
-
.
|
|
6591
|
-
|
|
6592
|
-
}
|
|
6594
|
+
/* R27 as amended by the PM 2026-10-07: the dock follows the same radii as every other surface (6px controls, 8px
|
|
6595
|
+
containers). The tab strip and the scratchpad are drawn with inline radii (workspace-cockpit-nav); the inline radii
|
|
6596
|
+
(8px tab strip, 6px controls) stay. A tab's colour dot is a dot and keeps its circle. */
|
|
6597
|
+
|
|
6593
6598
|
|
|
6594
6599
|
/* -- end of the layout sections: add nothing below this line -- */
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* The legacy Today view (view-today.js) calls mountToday on its own host, so both surfaces draw the one list.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { el } from './dom.js';
|
|
15
|
+
import { el, withAnchor } from './dom.js';
|
|
16
16
|
import { empty, tabs, button, toast, ensureComponentsCss } from './primitives.js';
|
|
17
17
|
import { registerTodaySource, todayCards, todayCount, openInitiative } from './registry.js';
|
|
18
18
|
import { repoTuples } from './panel-repos.js';
|
|
@@ -200,7 +200,8 @@ export function mountToday(host, ctx) {
|
|
|
200
200
|
if (out.sig === r.sig) return;
|
|
201
201
|
const k = focusKey(r.root);
|
|
202
202
|
r.sig = out.sig;
|
|
203
|
-
|
|
203
|
+
// a card added or removed above the reader must not move what they are reading (dom.js withAnchor)
|
|
204
|
+
withAnchor(host, r.root, 'data-today-id', () => r.root.replaceChildren(...out.children.filter(Boolean)));
|
|
204
205
|
refocus(r.root, k);
|
|
205
206
|
}
|
|
206
207
|
|
|
@@ -114,3 +114,63 @@ export function setChildren(parent, kids) {
|
|
|
114
114
|
});
|
|
115
115
|
return parent;
|
|
116
116
|
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Keep the reader's place across a rebuild (UI comfort, 2026-10-07: "when I scroll down into a section it pages up").
|
|
120
|
+
* Before `rebuild()` runs, the first element carrying `attr` that is still on screen in `scroller` is remembered with its
|
|
121
|
+
* offset from the scroller's top; afterwards the scroller is moved so that same element sits at that same offset. A
|
|
122
|
+
* card added or removed above the reader therefore no longer pushes what they are reading off the screen. With the
|
|
123
|
+
* scroller at its top, or no such element, it only runs `rebuild()`.
|
|
124
|
+
*/
|
|
125
|
+
export function withAnchor(scroller, root, attr, rebuild) {
|
|
126
|
+
let key = null;
|
|
127
|
+
let off = 0;
|
|
128
|
+
if (scroller && scroller.scrollTop > 0 && root) {
|
|
129
|
+
const top = scroller.getBoundingClientRect().top;
|
|
130
|
+
for (const n of root.querySelectorAll('[' + attr + ']')) {
|
|
131
|
+
const r = n.getBoundingClientRect();
|
|
132
|
+
if (r.height && r.bottom > top) { key = n.getAttribute(attr); off = r.top - top; break; }
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
rebuild();
|
|
136
|
+
if (key == null) return;
|
|
137
|
+
const again = [...root.querySelectorAll('[' + attr + ']')].find((n) => n.getAttribute(attr) === key);
|
|
138
|
+
if (!again) return;
|
|
139
|
+
const drift = again.getBoundingClientRect().top - scroller.getBoundingClientRect().top - off;
|
|
140
|
+
if (Math.abs(drift) >= 1) scroller.scrollTop += drift;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Give a scroller its reading position back when a rebuild took it away (UI comfort, 2026-10-07). A destination that
|
|
145
|
+
* redraws through a short state — a "Reading…" line, an emptied list — collapses the scroller, the browser clamps the
|
|
146
|
+
* offset towards the top, and when the content returns the reader is left at the top. This watches the scroller's
|
|
147
|
+
* subtree: when a change leaves the offset ABOVE where the reader last was and the content has shrunk below it, that
|
|
148
|
+
* place is held, and the next change that makes the content tall enough again returns to it. Anything the reader does
|
|
149
|
+
* (wheel, touch, a key, a click) drops what is held, so their own scrolling always wins. Returns its undo.
|
|
150
|
+
*/
|
|
151
|
+
export function keepScroll(scroller) {
|
|
152
|
+
let top = scroller.scrollTop;
|
|
153
|
+
let held = null;
|
|
154
|
+
let until = 0;
|
|
155
|
+
const drop = () => { held = null; };
|
|
156
|
+
const onScroll = () => { if (held == null) top = scroller.scrollTop; };
|
|
157
|
+
const intents = ['wheel', 'touchstart', 'keydown', 'pointerdown'];
|
|
158
|
+
for (const ev of intents) scroller.addEventListener(ev, drop, { passive: true, capture: true });
|
|
159
|
+
scroller.addEventListener('scroll', onScroll, { passive: true });
|
|
160
|
+
const mo = new MutationObserver(() => {
|
|
161
|
+
if (scroller.hidden || !scroller.isConnected) return;
|
|
162
|
+
const max = scroller.scrollHeight - scroller.clientHeight;
|
|
163
|
+
if (held == null) {
|
|
164
|
+
if (top > 0 && scroller.scrollTop < top - 1 && max < top) { held = top; until = Date.now() + 5000; }
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (Date.now() > until) { held = null; top = scroller.scrollTop; return; }
|
|
168
|
+
if (max >= held - 1) { scroller.scrollTop = held; top = held; held = null; }
|
|
169
|
+
});
|
|
170
|
+
mo.observe(scroller, { childList: true, subtree: true, characterData: true });
|
|
171
|
+
return () => {
|
|
172
|
+
mo.disconnect();
|
|
173
|
+
for (const ev of intents) scroller.removeEventListener(ev, drop, { capture: true });
|
|
174
|
+
scroller.removeEventListener('scroll', onScroll);
|
|
175
|
+
};
|
|
176
|
+
}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* env = { repo, ws, openInitiative(repo, slug), onData?(answer) }
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
import { el } from './dom.js';
|
|
23
|
+
import { el, withAnchor } from './dom.js';
|
|
24
24
|
import { card, iconButton, dragHandle, button, menu, empty, toast, SEL } from './primitives.js';
|
|
25
25
|
import { watchSig } from './sig-watch.js';
|
|
26
26
|
import { loadSortable, baseOptions, dragSet } from './bd-drag.js';
|
|
@@ -211,7 +211,7 @@ export function mountRoadmap(host, env) {
|
|
|
211
211
|
const kids = [headLine(), el('p', { class: 'in-note in-hint', 'data-in-hint': '' }, COPY.menuHintCard)];
|
|
212
212
|
if (data.flags && data.flags.length) kids.push(el('p', { class: 'in-note', 'data-in-flags': '' }, COPY.flags(data.flags.length)));
|
|
213
213
|
kids.push(el('div', { class: 'in-cols', 'data-in-cols': '' }, HORIZONS.map(column)));
|
|
214
|
-
root.replaceChildren(...kids);
|
|
214
|
+
withAnchor(root.closest('.fr-dest'), root, 'data-in-card', () => root.replaceChildren(...kids));
|
|
215
215
|
if (refocus) { const t = root.querySelector('[data-in-open="' + CSS.escape(refocus) + '"]'); if (t) t.focus({ preventScroll: true }); }
|
|
216
216
|
bindDrag();
|
|
217
217
|
}
|
|
@@ -36,6 +36,9 @@ export function unsyncedOf(st) {
|
|
|
36
36
|
&& ((st.behind | 0) > 0 || (st.ahead | 0) > 0 || (st.dirty_records | 0) > 0 || st.remote_stale === true);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/** The lucide symbol each place wears in the bar (UI comfort, 2026-10-07: a word alone is slow to find; the phone's tab bar already wears these). */
|
|
40
|
+
const NAV_ICON = Object.freeze({ today: 'house', mywork: 'list-checks', initiatives: 'target', board: 'square-kanban', reports: 'chart-column', docs: 'file-text', people: 'share-2', settings: 'settings' });
|
|
41
|
+
|
|
39
42
|
export function createTopbar({ go, openPalette, repoMenu, sync } = {}) {
|
|
40
43
|
const node = el('header', { class: 'fr-bar', 'data-shell': 'top', 'data-fr': 'bar' });
|
|
41
44
|
|
|
@@ -48,7 +51,7 @@ export function createTopbar({ go, openPalette, repoMenu, sync } = {}) {
|
|
|
48
51
|
const links = {};
|
|
49
52
|
for (const id of [...BAR_DESTS, 'settings']) {
|
|
50
53
|
links[id] = button({
|
|
51
|
-
label: COPY.nav[id], variant: 'ghost', icon: id
|
|
54
|
+
label: COPY.nav[id], variant: 'ghost', icon: NAV_ICON[id],
|
|
52
55
|
attrs: { class: 'fr-link', 'data-fr-dest': id, 'aria-current': 'false' },
|
|
53
56
|
onClick: () => go && go(id),
|
|
54
57
|
});
|
package/workspace/run-tests.js
CHANGED
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
* file in exactly one shard), and --check fails when a suite on disk is in no list, so a new suite
|
|
19
19
|
* that nobody wired in is a red build rather than a suite that runs nowhere.
|
|
20
20
|
*
|
|
21
|
-
* --changed
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
21
|
+
* --changed narrows the group to the suites a diff against REF (default origin/main) can reach, and
|
|
22
|
+
* runs the whole group whenever it cannot tell. The local inner loop uses it, and so does Workspace
|
|
23
|
+
* CI for the browser group on a PR (ci-browser-after-merge): every browser suite still runs after the
|
|
24
|
+
* merge on main, and kit-publish waits for that run. See changedSuites() for the rule.
|
|
25
25
|
*
|
|
26
26
|
* Output: a suite's own output is buffered and printed whole when it finishes, so lines from two
|
|
27
27
|
* suites never interleave. A passing suite prints its last lines; --verbose prints all of it.
|
|
@@ -256,7 +256,7 @@ async function main() {
|
|
|
256
256
|
if (changed) {
|
|
257
257
|
const ref = changed === true ? 'origin/main' : changed;
|
|
258
258
|
const pick = changedSuites(list, ref);
|
|
259
|
-
console.log(`run-tests: --changed against ${ref}: ${pick.why}. ${pick.suites.length} of ${list.length} suites run here;
|
|
259
|
+
console.log(`run-tests: --changed against ${ref}: ${pick.why}. ${pick.suites.length} of ${list.length} suites run here; every suite runs on main after the merge.`);
|
|
260
260
|
list = pick.suites;
|
|
261
261
|
if (!list.length) return;
|
|
262
262
|
}
|