@1agh/maude 1.4.0 → 1.4.1

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.
@@ -547,6 +547,16 @@ export interface FilePlane {
547
547
  doruceka(): Record<string, DeliveryState>;
548
548
  /** How many rows the ledger holds, before the doručenka's cap. */
549
549
  dorucekaTotal(): number;
550
+ /**
551
+ * The media this machine cannot place, in BYTES as well as files — T29.
552
+ * `null` when nothing is blocked, so a healthy project carries no field.
553
+ */
554
+ blocked(): {
555
+ files: number;
556
+ bytes: number;
557
+ unmeasured: number;
558
+ byClass: Record<string, { files: number; bytes: number }>;
559
+ } | null;
550
560
  }
551
561
 
552
562
  export function createFilePlane(opts: FilePlaneOptions): FilePlane {
@@ -2223,6 +2233,39 @@ export function createFilePlane(opts: FilePlaneOptions): FilePlane {
2223
2233
  dorucekaTotal() {
2224
2234
  return Object.keys(ledger.rows()).length;
2225
2235
  },
2236
+ /**
2237
+ * HOW MUCH MEDIA IS NOT HERE — plan T29.
2238
+ *
2239
+ * `failed` counts files, and a count is the wrong unit for the question
2240
+ * an operator is actually asking. "Nine files failed" is unreadable: nine
2241
+ * CSS sidecars is nothing, nine videos is the project. The bytes are what
2242
+ * says whether a person is looking at a broken thumbnail or at a project
2243
+ * whose media never arrived.
2244
+ *
2245
+ * Bounded by construction — one entry per blocked CLASS, and the classes
2246
+ * are a fixed set (`file-ledger.ts`), never per path. Sizes come from the
2247
+ * ledger's stat cache; a row we have never measured contributes to the
2248
+ * file count and nothing to the bytes, which is honest about not knowing
2249
+ * rather than guessing a size.
2250
+ */
2251
+ blocked() {
2252
+ const byClass: Record<string, { files: number; bytes: number }> = {};
2253
+ let files = 0;
2254
+ let bytes = 0;
2255
+ let unmeasured = 0;
2256
+ for (const row of Object.values(ledger.rows())) {
2257
+ if (row.state !== 'refused' && row.state !== 'stuck') continue;
2258
+ const cls = row.blockedClass ?? (row.state === 'stuck' ? 'unreachable' : 'refused');
2259
+ const size = typeof row.size === 'number' ? row.size : 0;
2260
+ if (!row.size) unmeasured += 1;
2261
+ byClass[cls] ??= { files: 0, bytes: 0 };
2262
+ byClass[cls].files += 1;
2263
+ byClass[cls].bytes += size;
2264
+ files += 1;
2265
+ bytes += size;
2266
+ }
2267
+ return files === 0 ? null : { files, bytes, unmeasured, byClass };
2268
+ },
2226
2269
  };
2227
2270
  }
2228
2271
 
@@ -4304,6 +4304,9 @@ export function createSyncRuntime(
4304
4304
  lastSeedProgress = progress;
4305
4305
  reportSeedProgress(progress, result);
4306
4306
  }
4307
+ // Read once: calling it inside the spread evaluated it twice and made
4308
+ // its `null` case the object's type.
4309
+ const blockedMedia = filePlane?.blocked() ?? null;
4307
4310
  statusStore?.updateFiles?.({
4308
4311
  ...fileTotals,
4309
4312
  pushed: filePushed,
@@ -4314,6 +4317,9 @@ export function createSyncRuntime(
4314
4317
  ...(filePlane.dorucekaTotal() > MAX_DORUCEKA_ROWS
4315
4318
  ? { deliveryTruncated: filePlane.dorucekaTotal() - MAX_DORUCEKA_ROWS }
4316
4319
  : {}),
4320
+ // T29 — the bytes, not just the count. Omitted entirely when
4321
+ // nothing is blocked, so a healthy project carries no field.
4322
+ ...(blockedMedia ? { blocked: blockedMedia } : {}),
4317
4323
  }
4318
4324
  : {}),
4319
4325
  ...(held.length > 0 ? { held } : {}),
@@ -159,6 +159,23 @@ export interface FilePlaneStatus {
159
159
  * added to cure, one lane over.
160
160
  */
161
161
  failed?: number;
162
+ /**
163
+ * HOW MUCH is not here — plan T29, the unit `failed` gets wrong.
164
+ *
165
+ * A count cannot answer the operator's question: nine blocked CSS sidecars
166
+ * is nothing and nine blocked videos is the project. `bytes` is what tells
167
+ * those apart. `byClass` is bounded by the ledger's fixed blocked-class set
168
+ * and never keyed by path; `unmeasured` says how many of the files have no
169
+ * size on record, so an under-count is visible rather than silent.
170
+ *
171
+ * Absent — not zero — when nothing is blocked.
172
+ */
173
+ blocked?: {
174
+ files: number;
175
+ bytes: number;
176
+ unmeasured: number;
177
+ byClass: Record<string, { files: number; bytes: number }>;
178
+ };
162
179
  /**
163
180
  * The hub asked us to slow down and the WHOLE LANE is paused until `until`.
164
181
  *
@@ -1,6 +1,15 @@
1
1
  {
2
2
  "$schema": "./whats-new.schema.json",
3
3
  "entries": [
4
+ {
5
+ "id": "history-on-a-team-project",
6
+ "version": "1.4.1",
7
+ "date": "2026-09-16",
8
+ "kind": "fix",
9
+ "title": "Your project history, from the status bar",
10
+ "summary": "On a project you were invited to, the status bar now opens the project history — every change with who made it, and Undo on the ones that are yours. It was only reachable from the View menu before.",
11
+ "surface": "design-ui"
12
+ },
4
13
  {
5
14
  "id": "design-system-on-every-desktop",
6
15
  "version": "1.4.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1agh/maude",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "description": "Marketplace of Claude Code plugins by Michal Dovrtěl: `design` (canvas-first design iteration) + `flow` (generic agentic workflow loop with .ai second brain). Ships the `maude` CLI (with `mdcc` legacy alias) to scaffold workspace, run the design dev server, and manage configs.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -29,7 +29,8 @@
29
29
  "test:e2e:desktop:git": "pnpm --filter @maude/desktop-e2e e2e:git",
30
30
  "build": "pnpm -r --if-present --filter '!@maude/desktop' '!@maude/desktop-e2e' run build",
31
31
  "build:binary": "bun run apps/studio/build.ts --release",
32
- "test": "node --test --test-reporter=spec cli/**/*.test.mjs && pnpm --filter @maude/hub test",
32
+ "test": "node --test --test-reporter=spec cli/**/*.test.mjs && pnpm --filter @maude/hub test && pnpm run test:harness",
33
+ "test:harness": "node --test $(find scripts/dev/sync-e2e -maxdepth 1 -name 'surface-*.test.mjs' ! -name 'surface-source-audit.test.mjs') && bun test scripts/dev/sync-e2e/surface-source-audit.test.mjs",
33
34
  "test:dev-server": "cd apps/studio && bun test --timeout 20000",
34
35
  "lint": "biome check .",
35
36
  "format": "biome format --write .",
@@ -53,16 +54,17 @@
53
54
  "test:e2e:desktop:parity": "pnpm --filter @maude/desktop-e2e e2e:parity",
54
55
  "test:e2e:desktop:sidecar-respawn": "pnpm --filter @maude/desktop-e2e e2e:sidecar-respawn",
55
56
  "test:e2e:desktop:team-project": "pnpm --filter @maude/desktop-e2e e2e:team-project",
57
+ "test:e2e:desktop:s20": "pnpm --filter @maude/desktop-e2e e2e:s20",
56
58
  "test:e2e:desktop:parity:cloud": "pnpm --filter @maude/desktop-e2e e2e:parity:cloud"
57
59
  },
58
60
  "optionalDependencies": {
59
- "@1agh/maude-darwin-arm64": "1.4.0",
60
- "@1agh/maude-darwin-x64": "1.4.0",
61
- "@1agh/maude-linux-arm64": "1.4.0",
62
- "@1agh/maude-linux-arm64-musl": "1.4.0",
63
- "@1agh/maude-linux-x64": "1.4.0",
64
- "@1agh/maude-linux-x64-musl": "1.4.0",
65
- "@1agh/maude-win32-x64": "1.4.0"
61
+ "@1agh/maude-darwin-arm64": "1.4.1",
62
+ "@1agh/maude-darwin-x64": "1.4.1",
63
+ "@1agh/maude-linux-arm64": "1.4.1",
64
+ "@1agh/maude-linux-arm64-musl": "1.4.1",
65
+ "@1agh/maude-linux-x64": "1.4.1",
66
+ "@1agh/maude-linux-x64-musl": "1.4.1",
67
+ "@1agh/maude-win32-x64": "1.4.1"
66
68
  },
67
69
  "files": [
68
70
  "cli",