Train BM8 #862

Closed
kayg wants to merge 3 commits from train/2026-11-02-bm8 into dev
Owner

Assembles all 16 requested current PR heads on dev with ordered --no-ff merges, including the corrected sharing, restart, tasks and test fixtures, plus the search palette port. Replaces #838. Mechanical conflict resolutions preserve both behaviors. No merge into dev.

Final validation report, included heads, decisions and artifacts. Artifacts live in PR comments under ADR 0003.

Assembles all 16 requested current PR heads on dev with ordered --no-ff merges, including the corrected sharing, restart, tasks and test fixtures, plus the search palette port. Replaces #838. Mechanical conflict resolutions preserve both behaviors. No merge into dev. [Final validation report, included heads, decisions and artifacts](https://git.kayg.org/kayg/calternal/pulls/862#issuecomment-75185). Artifacts live in PR comments under ADR 0003.
docs: link historical CI allocation observations
Some checks failed
CI / server (pull_request) Has been cancelled
CI / rust (pull_request) Has been cancelled
CI / web (pull_request) Has been cancelled
Markdown extraction contract / markdown-native-contract (pull_request) Has been cancelled
Markdown extraction contract / markdown-editor-contract (pull_request) Has been cancelled
9c05d871a1
Train BM8: merge #858 (9c05d871)
Some checks failed
CI / server (pull_request) Has been cancelled
CI / rust (pull_request) Has been cancelled
CI / web (pull_request) Has been cancelled
Markdown extraction contract / markdown-native-contract (pull_request) Has been cancelled
Markdown extraction contract / markdown-editor-contract (pull_request) Has been cancelled
ac1a23dde8
Author
Owner

Assembly artifact cleanup: #831 adds two attachment flush measurement artifacts after #858. Moving their contents here under ADR 0003; only these artifacts are removed from the final train tree. No implementation changes.

docs/perf/attachment-index-flush.md

# Attachment index flushes

## Plan and cost

Keep one SQLite connection per recent user in a bounded cache of 64 entries.
The duplicate lookup and optional row insert use that shared connection. Use
WAL with `synchronous=FULL`, so a changed row is durable in the WAL before the
action is acknowledged; SQLite can checkpoint it later instead of flushing the
database and directory on every upload. Leave the attachment file fsync and
its parent-directory fsync unchanged. A new file gets one index write and one
WAL sync; a duplicate or an action with no attachment change gets no index
write or sync.

The lookup remains O(log F) for F indexed files. Cache lookup is O(1), with an
O(64) least-recently-used update, and at most 64 cached connections are open.
If all entries are in use when the cache is full, the action uses a temporary
connection; SQLite still commits the row durably, with an extra close-time
checkpoint possible in that case. The stronger FULL setting preserves the
index row's durability while deferring database checkpoint work.

| Failure | Effect | Detection | Recovery | Test |
|---|---|---|---|---|
| Index open or duplicate lookup fails | The add action fails before placement | Returned action error | Retry with the same idempotency key | Existing attachment action tests |
| Index row insert fails after placement | The file remains durable; the index remains a hint | Existing warning | A later upload can retry the index write | Existing upload and retry tests |
| No attachment changed | No index row is written or flushed | `strace` count between save markers | No recovery needed | Linux flush-count regression test |
| One attachment changed | One row is committed with one WAL sync | `strace` count between save markers | SQLite's FULL-synced WAL is replayed after restart | Linux flush-count regression test |
| Cache is full and all entries are active | One action uses a temporary connection and may checkpoint at close | Cache-full debug event and sync trace | Reopen the per-user index on the next action | Bounded-cache test and Linux flush-count regression test |

Gate tests: `cargo test --locked -p calternal-attachments -j4`; the Linux
flush-count regression test; the unchanged `1k / SSD / 4 clients / 256 KiB`
matrix cell on dev-01 before and after; and one matched baseline/head pair on
perf-01 under `~/bin/perf-lock`. The perf budgets stay unchanged.

## Baseline on current dev

Commit: `55f35fcde3bc91ca3781055577b93e93819f6cd4`. The server was built on
dev-01 as `~/bin/calternal-dev-55f35fc` (SHA256
`9e3269898ce6bd45e3ef9326149f41995d176c26394841ef9eef376d2b1f5dc4`). The
matrix driver was `~/bin/calternal-perf-0f38de31` (SHA256
`e83e22618a1ce06bc4bc42ee16ed966eff0fe4ff922c42da1cc42ae4f8700a8b`, built
from the #788 matrix source). It used seed `75931004`, one 1k-note Home, four
upload clients, 256 KiB unique attachments, 60 seconds, and 60 overlapping
note saves.

The plain run used:

```sh
calternal-perf --bin ~/bin/calternal-dev-55f35fc --work <run>/work \
  --sizes 1k --runs 1 --cold-runs 0 --idle-secs 0 --soak-secs 0 \
  --seed 75931004 --upload-sizes 256k --upload-clients 4 \
  --upload-secs 60 --upload-only --json <run>/result.json --gate

The trace run used the same arguments under:

nix shell nixpkgs#strace -c strace -f -tt -T -yy \
  -e trace=fsync,fdatasync,openat,close,renameat2,rename,fcntl \
  -o <run>/trace <calternal-perf command above>

Untraced warm run: 696 uploads; 60 save acknowledgements; p50 17.88 ms and p95
56.26 ms. This is a single dev-01 diagnostic run, not perf-VM qualification.

The matching strace -f -tt -T -yy run completed 465 uploads. It found 932
attachment WAL syncs, 469 attachment database syncs, and 467 attachment
directory syncs: 1,868 total, or 4.017 attachment-index sync calls per upload.
Two WAL syncs, one database sync, and one directory sync occurred per new
attachment. The trace also showed a separate pending-marker sync per upload.
Traced latency is excluded from the before/after comparison.

This reproduces the inherited amplification reported in
#788 comment 73181
on current dev. The original profile at 133cf2c2 is not used as the current
baseline.

Fix and current-dev result

The implementation keeps up to 64 per-user index connections in an LRU cache.
The connection uses WAL with synchronous=FULL, so each changed row is durable
in the WAL before acknowledgement. SQLite can checkpoint after its normal
page threshold, cache eviction, or shutdown. Attachment bytes still use the
existing file and parent-directory durability barriers.

The Linux regression test measures sync syscalls between real upload action
markers under strace. It passed with 0 attachment-index sync calls for a
duplicate/no-change save and 1 WAL sync call for one new attachment. The
bounded-cache test also passed with 65 busy users: 64 cached connections and
one scoped connection.

On dev-01, the same untraced matrix cell after the change completed 1,520
uploads and 59 overlapping note saves. Save-ack p50 was 13.29 ms and p95 was
40.62 ms. Against the single baseline run, p50 improved 25.7% and p95 improved
27.8%. These shared-host samples are diagnostic only, not qualification.

The matching trace run completed 807 uploads and 60 save samples. It found
814 attachment WAL syncs, 5 database syncs, and 2 attachment-directory syncs:
821 total, or 1.017 attachment-index sync calls per upload. The seven calls
above one per upload came from SQLite checkpoints during the run. Traced
save-ack p95 was 58.37 ms and is excluded from the latency comparison.

Targeted gates on dev-01 passed:

cargo fmt --all --check
cargo test --locked -p calternal-attachments -j4 -- --nocapture
cargo clippy --locked -p calternal-attachments --all-targets -j4 -- -D warnings
cargo build --release --locked -p calternal -j4

The test result was 21 passed, 0 failed; targeted Clippy passed with warnings
denied. The current-dev binary was built from implementation commit
eef6473203e20df1be29346b8ef03ff47a269522 and has SHA256
196ff9b8ea473f0cf22ea0546c76056d6efb2d413317ec0a8c6063a759271bbd.

Perf-01 matched comparison

On perf-01, I ran five alternating matched pairs with the same 1k-note Home,
four upload clients, 256 KiB attachments, 60-second upload window, and seed
75931004. Homes and app state were on the VM's ext4 non-rotational SSD. The
baseline was current origin/dev at 55f35fcde3bc91ca3781055577b93e93819f6cd4;
the fixed binary was built from eef6473203e20df1be29346b8ef03ff47a269522.
Both were measured with the same matrix driver and unchanged budgets file
(SHA256 8feee55baeb3940c09015f37db8da85bd0cf2c5f33d5673568bd976fcf02ace3).
Each run exited 0 and collected 59 or 60 save acknowledgements.

Pair Order Base save p50 / p95 Fixed save p50 / p95 Save p95 ratio Response p95 ratio Throughput ratio
1 base / fixed 8.05 / 11.28 ms 9.95 / 16.15 ms 143.2% 98.3% 114.4%
2 fixed / base 11.69 / 20.98 ms 12.20 / 20.45 ms 97.5% 47.8% 159.9%
3 base / fixed 12.36 / 17.88 ms 12.03 / 17.67 ms 98.8% 70.4% 147.3%
4 fixed / base 11.90 / 18.94 ms 12.56 / 18.33 ms 96.8% 66.6% 148.9%
5 base / fixed 11.70 / 20.47 ms 13.23 / 22.93 ms 112.0% 68.0% 150.4%
Median 11.70 / 18.94 ms 12.20 / 18.33 ms 98.8% paired 68.0% paired 148.9% paired

The five-pair median meets the existing #788 small all-SSD cell limits: upload
throughput at least 95% of current dev, upload response p95 at most 105%, and
overlapping save-ack p95 at most 105%. Save-ack p50 was 4.2% higher by the
median of run p50s; median run p95 fell 3.2%, and the median paired p95 ratio
was 98.8%. The first and fifth save-ack pairs were noisy and slower on the
fixed run, which is why the full five-pair table is retained. The full 18-cell
matrix still needs requalification after rebasing #788.

The unchanged perf/budgets.json was passed to --gate; it has no entry for
uploads.256KiB.save_ack, so the upload comparison uses the existing relative
matrix limits from #788 comment 72886.
No budget was edited. Individual JSON and Markdown outputs are retained on
perf-01. The compact per-run data is in
attachment-index-flush-perf01.json.


### docs/perf/attachment-index-flush-perf01.json
```json
{
  "machine": "perf-01: Linux 6.12.113+deb13-amd64, 4 CPUs, 7.7 GiB RAM, ext4 non-rotational SSD for Homes and state",
  "baseline": {
    "origin_dev": "55f35fcde3bc91ca3781055577b93e93819f6cd4",
    "binary_sha256": "9e3269898ce6bd45e3ef9326149f41995d176c26394841ef9eef376d2b1f5dc4"
  },
  "fixed": {
    "implementation_commit": "eef6473203e20df1be29346b8ef03ff47a269522",
    "binary_sha256": "196ff9b8ea473f0cf22ea0546c76056d6efb2d413317ec0a8c6063a759271bbd"
  },
  "matrix_driver_sha256": "e83e22618a1ce06bc4bc42ee16ed966eff0fe4ff922c42da1cc42ae4f8700a8b",
  "config": {
    "notes": 1000,
    "upload_bytes": 262144,
    "upload_clients": 4,
    "upload_seconds": 60,
    "seed": 75931004,
    "matched_pairs": 5,
    "order": [
      "base/fixed",
      "fixed/base",
      "base/fixed",
      "fixed/base",
      "base/fixed"
    ]
  },
  "command": "calternal-perf --bin <base-or-fixed-server> --work <run>/work --sizes 1k --runs 1 --cold-runs 0 --idle-secs 0 --soak-secs 0 --seed 75931004 --upload-sizes 256k --upload-clients 4 --upload-secs 60 --upload-only --json <run>/result.json --md <run>/result.md --budgets <unchanged perf/budgets.json> --gate",
  "budget_file_sha256": "8feee55baeb3940c09015f37db8da85bd0cf2c5f33d5673568bd976fcf02ace3",
  "matrix_thresholds": {
    "throughput_ratio_pct_min": 95,
    "upload_response_p95_ratio_pct_max": 105,
    "save_ack_p95_ratio_pct_max": 105
  },
  "pairs": [
    {
      "pair": 1,
      "order": "base/fixed",
      "base": {
        "save_ack_ms": {
          "p50": 8.051699000000001,
          "p95": 11.276453,
          "samples": 60
        },
        "upload_response_ms": {
          "p50": 122.414671,
          "p95": 143.195845,
          "samples": 1924
        },
        "throughput_mib_s": 7.8760662691913295,
        "exit_code": 0
      },
      "fixed": {
        "save_ack_ms": {
          "p50": 9.95182,
          "p95": 16.15159,
          "samples": 59
        },
        "upload_response_ms": {
          "p50": 116.10380400000001,
          "p95": 140.805797,
          "samples": 2201
        },
        "throughput_mib_s": 9.010913115334635,
        "exit_code": 0
      },
      "paired_ratio_pct": {
        "save_ack_p95": 143.23289424431601,
        "upload_response_p95": 98.33092363818238,
        "throughput": 114.40880266056756
      }
    },
    {
      "pair": 2,
      "order": "fixed/base",
      "base": {
        "save_ack_ms": {
          "p50": 11.694977,
          "p95": 20.984858,
          "samples": 60
        },
        "upload_response_ms": {
          "p50": 181.23751900000002,
          "p95": 305.759208,
          "samples": 1220
        },
        "throughput_mib_s": 4.994277119581212,
        "exit_code": 0
      },
      "fixed": {
        "save_ack_ms": {
          "p50": 12.195746,
          "p95": 20.453059999999997,
          "samples": 59
        },
        "upload_response_ms": {
          "p50": 122.14196,
          "p95": 146.29314,
          "samples": 1951
        },
        "throughput_mib_s": 7.987411446142995,
        "exit_code": 0
      },
      "paired_ratio_pct": {
        "save_ack_p95": 97.46580129348503,
        "upload_response_p95": 47.84586569180281,
        "throughput": 159.93128244378977
      }
    },
    {
      "pair": 3,
      "order": "base/fixed",
      "base": {
        "save_ack_ms": {
          "p50": 12.356477,
          "p95": 17.879756999999998,
          "samples": 60
        },
        "upload_response_ms": {
          "p50": 179.02521199999998,
          "p95": 211.695008,
          "samples": 1317
        },
        "throughput_mib_s": 5.391684547783172,
        "exit_code": 0
      },
      "fixed": {
        "save_ack_ms": {
          "p50": 12.034103,
          "p95": 17.673111,
          "samples": 60
        },
        "upload_response_ms": {
          "p50": 123.075991,
          "p95": 149.01044299999998,
          "samples": 1940
        },
        "throughput_mib_s": 7.942181138908136,
        "exit_code": 0
      },
      "paired_ratio_pct": {
        "save_ack_p95": 98.84424603757199,
        "upload_response_p95": 70.38920965013969,
        "throughput": 147.30426211922241
      }
    },
    {
      "pair": 4,
      "order": "fixed/base",
      "base": {
        "save_ack_ms": {
          "p50": 11.903978,
          "p95": 18.941137,
          "samples": 60
        },
        "upload_response_ms": {
          "p50": 179.25838,
          "p95": 220.128913,
          "samples": 1307
        },
        "throughput_mib_s": 5.350753287242168,
        "exit_code": 0
      },
      "fixed": {
        "save_ack_ms": {
          "p50": 12.555529,
          "p95": 18.334640999999998,
          "samples": 60
        },
        "upload_response_ms": {
          "p50": 122.91566,
          "p95": 146.669366,
          "samples": 1946
        },
        "throughput_mib_s": 7.967140604998429,
        "exit_code": 0
      },
      "paired_ratio_pct": {
        "save_ack_p95": 96.79799581197263,
        "upload_response_p95": 66.6288512495403,
        "throughput": 148.8975510045385
      }
    },
    {
      "pair": 5,
      "order": "base/fixed",
      "base": {
        "save_ack_ms": {
          "p50": 11.702833,
          "p95": 20.466426,
          "samples": 60
        },
        "upload_response_ms": {
          "p50": 179.29509800000002,
          "p95": 215.944592,
          "samples": 1311
        },
        "throughput_mib_s": 5.367088973256576,
        "exit_code": 0
      },
      "fixed": {
        "save_ack_ms": {
          "p50": 13.234993000000001,
          "p95": 22.926043,
          "samples": 59
        },
        "upload_response_ms": {
          "p50": 123.30149399999999,
          "p95": 146.776267,
          "samples": 1940
        },
        "throughput_mib_s": 8.074598087107052,
        "exit_code": 0
      },
      "paired_ratio_pct": {
        "save_ack_p95": 112.01781395540189,
        "upload_response_p95": 67.96941087554534,
        "throughput": 150.446510712634
      }
    }
  ],
  "summary": {
    "base": {
      "save_ack_p50_ms": 11.702833,
      "save_ack_p95_ms": 18.941137,
      "upload_response_p95_ms": 215.944592,
      "throughput_mib_s": 5.367088973256576
    },
    "fixed": {
      "save_ack_p50_ms": 12.195746,
      "save_ack_p95_ms": 18.334640999999998,
      "upload_response_p95_ms": 146.669366,
      "throughput_mib_s": 7.987411446142995
    },
    "median_paired_ratio_pct": {
      "save_ack_p95": 98.84424603757199,
      "upload_response_p95": 67.96941087554534,
      "throughput": 148.8975510045385
    }
  }
}

Assembly artifact cleanup: #831 adds two attachment flush measurement artifacts after #858. Moving their contents here under ADR 0003; only these artifacts are removed from the final train tree. No implementation changes. ### docs/perf/attachment-index-flush.md ```markdown # Attachment index flushes ## Plan and cost Keep one SQLite connection per recent user in a bounded cache of 64 entries. The duplicate lookup and optional row insert use that shared connection. Use WAL with `synchronous=FULL`, so a changed row is durable in the WAL before the action is acknowledged; SQLite can checkpoint it later instead of flushing the database and directory on every upload. Leave the attachment file fsync and its parent-directory fsync unchanged. A new file gets one index write and one WAL sync; a duplicate or an action with no attachment change gets no index write or sync. The lookup remains O(log F) for F indexed files. Cache lookup is O(1), with an O(64) least-recently-used update, and at most 64 cached connections are open. If all entries are in use when the cache is full, the action uses a temporary connection; SQLite still commits the row durably, with an extra close-time checkpoint possible in that case. The stronger FULL setting preserves the index row's durability while deferring database checkpoint work. | Failure | Effect | Detection | Recovery | Test | |---|---|---|---|---| | Index open or duplicate lookup fails | The add action fails before placement | Returned action error | Retry with the same idempotency key | Existing attachment action tests | | Index row insert fails after placement | The file remains durable; the index remains a hint | Existing warning | A later upload can retry the index write | Existing upload and retry tests | | No attachment changed | No index row is written or flushed | `strace` count between save markers | No recovery needed | Linux flush-count regression test | | One attachment changed | One row is committed with one WAL sync | `strace` count between save markers | SQLite's FULL-synced WAL is replayed after restart | Linux flush-count regression test | | Cache is full and all entries are active | One action uses a temporary connection and may checkpoint at close | Cache-full debug event and sync trace | Reopen the per-user index on the next action | Bounded-cache test and Linux flush-count regression test | Gate tests: `cargo test --locked -p calternal-attachments -j4`; the Linux flush-count regression test; the unchanged `1k / SSD / 4 clients / 256 KiB` matrix cell on dev-01 before and after; and one matched baseline/head pair on perf-01 under `~/bin/perf-lock`. The perf budgets stay unchanged. ## Baseline on current dev Commit: `55f35fcde3bc91ca3781055577b93e93819f6cd4`. The server was built on dev-01 as `~/bin/calternal-dev-55f35fc` (SHA256 `9e3269898ce6bd45e3ef9326149f41995d176c26394841ef9eef376d2b1f5dc4`). The matrix driver was `~/bin/calternal-perf-0f38de31` (SHA256 `e83e22618a1ce06bc4bc42ee16ed966eff0fe4ff922c42da1cc42ae4f8700a8b`, built from the #788 matrix source). It used seed `75931004`, one 1k-note Home, four upload clients, 256 KiB unique attachments, 60 seconds, and 60 overlapping note saves. The plain run used: ```sh calternal-perf --bin ~/bin/calternal-dev-55f35fc --work <run>/work \ --sizes 1k --runs 1 --cold-runs 0 --idle-secs 0 --soak-secs 0 \ --seed 75931004 --upload-sizes 256k --upload-clients 4 \ --upload-secs 60 --upload-only --json <run>/result.json --gate ``` The trace run used the same arguments under: ```sh nix shell nixpkgs#strace -c strace -f -tt -T -yy \ -e trace=fsync,fdatasync,openat,close,renameat2,rename,fcntl \ -o <run>/trace <calternal-perf command above> ``` Untraced warm run: 696 uploads; 60 save acknowledgements; p50 17.88 ms and p95 56.26 ms. This is a single dev-01 diagnostic run, not perf-VM qualification. The matching `strace -f -tt -T -yy` run completed 465 uploads. It found 932 attachment WAL syncs, 469 attachment database syncs, and 467 attachment directory syncs: 1,868 total, or 4.017 attachment-index sync calls per upload. Two WAL syncs, one database sync, and one directory sync occurred per new attachment. The trace also showed a separate pending-marker sync per upload. Traced latency is excluded from the before/after comparison. This reproduces the inherited amplification reported in [#788 comment 73181](https://git.kayg.org/kayg/calternal/pulls/788#issuecomment-73181) on current dev. The original profile at `133cf2c2` is not used as the current baseline. ## Fix and current-dev result The implementation keeps up to 64 per-user index connections in an LRU cache. The connection uses WAL with `synchronous=FULL`, so each changed row is durable in the WAL before acknowledgement. SQLite can checkpoint after its normal page threshold, cache eviction, or shutdown. Attachment bytes still use the existing file and parent-directory durability barriers. The Linux regression test measures sync syscalls between real upload action markers under `strace`. It passed with **0 attachment-index sync calls** for a duplicate/no-change save and **1 WAL sync call** for one new attachment. The bounded-cache test also passed with 65 busy users: 64 cached connections and one scoped connection. On dev-01, the same untraced matrix cell after the change completed 1,520 uploads and 59 overlapping note saves. Save-ack p50 was 13.29 ms and p95 was 40.62 ms. Against the single baseline run, p50 improved 25.7% and p95 improved 27.8%. These shared-host samples are diagnostic only, not qualification. The matching trace run completed 807 uploads and 60 save samples. It found 814 attachment WAL syncs, 5 database syncs, and 2 attachment-directory syncs: 821 total, or 1.017 attachment-index sync calls per upload. The seven calls above one per upload came from SQLite checkpoints during the run. Traced save-ack p95 was 58.37 ms and is excluded from the latency comparison. Targeted gates on dev-01 passed: ```sh cargo fmt --all --check cargo test --locked -p calternal-attachments -j4 -- --nocapture cargo clippy --locked -p calternal-attachments --all-targets -j4 -- -D warnings cargo build --release --locked -p calternal -j4 ``` The test result was 21 passed, 0 failed; targeted Clippy passed with warnings denied. The current-dev binary was built from implementation commit `eef6473203e20df1be29346b8ef03ff47a269522` and has SHA256 `196ff9b8ea473f0cf22ea0546c76056d6efb2d413317ec0a8c6063a759271bbd`. ## Perf-01 matched comparison On perf-01, I ran five alternating matched pairs with the same 1k-note Home, four upload clients, 256 KiB attachments, 60-second upload window, and seed `75931004`. Homes and app state were on the VM's ext4 non-rotational SSD. The baseline was current `origin/dev` at `55f35fcde3bc91ca3781055577b93e93819f6cd4`; the fixed binary was built from `eef6473203e20df1be29346b8ef03ff47a269522`. Both were measured with the same matrix driver and unchanged budgets file (SHA256 `8feee55baeb3940c09015f37db8da85bd0cf2c5f33d5673568bd976fcf02ace3`). Each run exited 0 and collected 59 or 60 save acknowledgements. | Pair | Order | Base save p50 / p95 | Fixed save p50 / p95 | Save p95 ratio | Response p95 ratio | Throughput ratio | |---:|---|---:|---:|---:|---:|---:| | 1 | base / fixed | 8.05 / 11.28 ms | 9.95 / 16.15 ms | 143.2% | 98.3% | 114.4% | | 2 | fixed / base | 11.69 / 20.98 ms | 12.20 / 20.45 ms | 97.5% | 47.8% | 159.9% | | 3 | base / fixed | 12.36 / 17.88 ms | 12.03 / 17.67 ms | 98.8% | 70.4% | 147.3% | | 4 | fixed / base | 11.90 / 18.94 ms | 12.56 / 18.33 ms | 96.8% | 66.6% | 148.9% | | 5 | base / fixed | 11.70 / 20.47 ms | 13.23 / 22.93 ms | 112.0% | 68.0% | 150.4% | | Median | | 11.70 / 18.94 ms | 12.20 / 18.33 ms | 98.8% paired | 68.0% paired | 148.9% paired | The five-pair median meets the existing #788 small all-SSD cell limits: upload throughput at least 95% of current dev, upload response p95 at most 105%, and overlapping save-ack p95 at most 105%. Save-ack p50 was 4.2% higher by the median of run p50s; median run p95 fell 3.2%, and the median paired p95 ratio was 98.8%. The first and fifth save-ack pairs were noisy and slower on the fixed run, which is why the full five-pair table is retained. The full 18-cell matrix still needs requalification after rebasing #788. The unchanged `perf/budgets.json` was passed to `--gate`; it has no entry for `uploads.256KiB.save_ack`, so the upload comparison uses the existing relative matrix limits from [#788 comment 72886](https://git.kayg.org/kayg/calternal/pulls/788#issuecomment-72886). No budget was edited. Individual JSON and Markdown outputs are retained on perf-01. The compact per-run data is in [`attachment-index-flush-perf01.json`](attachment-index-flush-perf01.json). ``` ### docs/perf/attachment-index-flush-perf01.json ```json { "machine": "perf-01: Linux 6.12.113+deb13-amd64, 4 CPUs, 7.7 GiB RAM, ext4 non-rotational SSD for Homes and state", "baseline": { "origin_dev": "55f35fcde3bc91ca3781055577b93e93819f6cd4", "binary_sha256": "9e3269898ce6bd45e3ef9326149f41995d176c26394841ef9eef376d2b1f5dc4" }, "fixed": { "implementation_commit": "eef6473203e20df1be29346b8ef03ff47a269522", "binary_sha256": "196ff9b8ea473f0cf22ea0546c76056d6efb2d413317ec0a8c6063a759271bbd" }, "matrix_driver_sha256": "e83e22618a1ce06bc4bc42ee16ed966eff0fe4ff922c42da1cc42ae4f8700a8b", "config": { "notes": 1000, "upload_bytes": 262144, "upload_clients": 4, "upload_seconds": 60, "seed": 75931004, "matched_pairs": 5, "order": [ "base/fixed", "fixed/base", "base/fixed", "fixed/base", "base/fixed" ] }, "command": "calternal-perf --bin <base-or-fixed-server> --work <run>/work --sizes 1k --runs 1 --cold-runs 0 --idle-secs 0 --soak-secs 0 --seed 75931004 --upload-sizes 256k --upload-clients 4 --upload-secs 60 --upload-only --json <run>/result.json --md <run>/result.md --budgets <unchanged perf/budgets.json> --gate", "budget_file_sha256": "8feee55baeb3940c09015f37db8da85bd0cf2c5f33d5673568bd976fcf02ace3", "matrix_thresholds": { "throughput_ratio_pct_min": 95, "upload_response_p95_ratio_pct_max": 105, "save_ack_p95_ratio_pct_max": 105 }, "pairs": [ { "pair": 1, "order": "base/fixed", "base": { "save_ack_ms": { "p50": 8.051699000000001, "p95": 11.276453, "samples": 60 }, "upload_response_ms": { "p50": 122.414671, "p95": 143.195845, "samples": 1924 }, "throughput_mib_s": 7.8760662691913295, "exit_code": 0 }, "fixed": { "save_ack_ms": { "p50": 9.95182, "p95": 16.15159, "samples": 59 }, "upload_response_ms": { "p50": 116.10380400000001, "p95": 140.805797, "samples": 2201 }, "throughput_mib_s": 9.010913115334635, "exit_code": 0 }, "paired_ratio_pct": { "save_ack_p95": 143.23289424431601, "upload_response_p95": 98.33092363818238, "throughput": 114.40880266056756 } }, { "pair": 2, "order": "fixed/base", "base": { "save_ack_ms": { "p50": 11.694977, "p95": 20.984858, "samples": 60 }, "upload_response_ms": { "p50": 181.23751900000002, "p95": 305.759208, "samples": 1220 }, "throughput_mib_s": 4.994277119581212, "exit_code": 0 }, "fixed": { "save_ack_ms": { "p50": 12.195746, "p95": 20.453059999999997, "samples": 59 }, "upload_response_ms": { "p50": 122.14196, "p95": 146.29314, "samples": 1951 }, "throughput_mib_s": 7.987411446142995, "exit_code": 0 }, "paired_ratio_pct": { "save_ack_p95": 97.46580129348503, "upload_response_p95": 47.84586569180281, "throughput": 159.93128244378977 } }, { "pair": 3, "order": "base/fixed", "base": { "save_ack_ms": { "p50": 12.356477, "p95": 17.879756999999998, "samples": 60 }, "upload_response_ms": { "p50": 179.02521199999998, "p95": 211.695008, "samples": 1317 }, "throughput_mib_s": 5.391684547783172, "exit_code": 0 }, "fixed": { "save_ack_ms": { "p50": 12.034103, "p95": 17.673111, "samples": 60 }, "upload_response_ms": { "p50": 123.075991, "p95": 149.01044299999998, "samples": 1940 }, "throughput_mib_s": 7.942181138908136, "exit_code": 0 }, "paired_ratio_pct": { "save_ack_p95": 98.84424603757199, "upload_response_p95": 70.38920965013969, "throughput": 147.30426211922241 } }, { "pair": 4, "order": "fixed/base", "base": { "save_ack_ms": { "p50": 11.903978, "p95": 18.941137, "samples": 60 }, "upload_response_ms": { "p50": 179.25838, "p95": 220.128913, "samples": 1307 }, "throughput_mib_s": 5.350753287242168, "exit_code": 0 }, "fixed": { "save_ack_ms": { "p50": 12.555529, "p95": 18.334640999999998, "samples": 60 }, "upload_response_ms": { "p50": 122.91566, "p95": 146.669366, "samples": 1946 }, "throughput_mib_s": 7.967140604998429, "exit_code": 0 }, "paired_ratio_pct": { "save_ack_p95": 96.79799581197263, "upload_response_p95": 66.6288512495403, "throughput": 148.8975510045385 } }, { "pair": 5, "order": "base/fixed", "base": { "save_ack_ms": { "p50": 11.702833, "p95": 20.466426, "samples": 60 }, "upload_response_ms": { "p50": 179.29509800000002, "p95": 215.944592, "samples": 1311 }, "throughput_mib_s": 5.367088973256576, "exit_code": 0 }, "fixed": { "save_ack_ms": { "p50": 13.234993000000001, "p95": 22.926043, "samples": 59 }, "upload_response_ms": { "p50": 123.30149399999999, "p95": 146.776267, "samples": 1940 }, "throughput_mib_s": 8.074598087107052, "exit_code": 0 }, "paired_ratio_pct": { "save_ack_p95": 112.01781395540189, "upload_response_p95": 67.96941087554534, "throughput": 150.446510712634 } } ], "summary": { "base": { "save_ack_p50_ms": 11.702833, "save_ack_p95_ms": 18.941137, "upload_response_p95_ms": 215.944592, "throughput_mib_s": 5.367088973256576 }, "fixed": { "save_ack_p50_ms": 12.195746, "save_ack_p95_ms": 18.334640999999998, "upload_response_p95_ms": 146.669366, "throughput_mib_s": 7.987411446142995 }, "median_paired_ratio_pct": { "save_ack_p95": 98.84424603757199, "upload_response_p95": 67.96941087554534, "throughput": 148.8975510045385 } } } ```
Author
Owner

Assembly complete. Head 4d02e704a281dda00655f45aca6d0d6744fb6357 (full head recorded by gates), base 55f35fcde3bc91ca3781055577b93e93819f6cd4. All requested PRs included; none excluded. Captured current heads at assembly time:

PR Source head Merge
#779 7b97e4d14231be07f6a80cb9ca172d02442c487e f8931e0d3081da14f79b5639e33d16c5591b565b
#858 9c05d871a1692df46eb9e06e0e386bdf8c0bbd9c ac1a23dde8ab32f7033d5b58194b7d25caa99a57
#824 f1c1aa961e169ef99e132063a97ccbd9a89a5def 6e5e659044873b52d95cdca6ee6d4df765ef6a0c
#827 91a19dcf0a9cf3844861e0e6acf4318618f3ea33 c3a626df5418c3b5a1d4e73e4965b0bede7ab925
#829 d455842942731aabc05474c7929597d57090a7cc 807d3b6955df8b6e972fe897f9240cac2e0c4c85
#835 19071be8455d718822ba98f6b9a402c5fd345242 f8268ea5f1be46a2aef2ce04efa83c6c0adcd0fe
#828 5d7b9b36cd008de78851809724c90a85d92b40a7 d3f297b719c4ab851aee749d3a0e3f3737e3624a
#833 ccaece3dfecbe2048634fc792d7d4a2c6e31f324 b2a141ea1553ddd8c8a1de197fb3bd2aa686f60e
#836 5f7d1a59776411e7f3353d0023817b43affae51e 7959619d8ecf3c885d877dc3557cd52fd8eefbb6
#831 316b43e9578c3e4d2944df8df43caebc611356f7 110abecb403bfc6b9e8fd9654a46892c55f62931
#844 50cce9dcceb87f23a9f346300b3336e315ff6c0c 9abec9665d4009563edf380e4ae688c1ea68857e
#847 40448327a15730271d56226b898e48bcc87b2a4d 49ff52c50f6bd9e750c416924783eec7e95a1932
#855 c38c3083d51f4adcf5cd76a70f1f6960b427413e 00b4107c2d713204d8b09ac5f4ed54ec77077aad
#857 144e717145e35f70356f593b7c04a527e00f7b17 02e53ae075e78e4b49be7881459a7ab59f4097ce
#856 85845e3d11bea351608bde7481779ba2303372e5 09bd556a905eac10bb1e4232655220f1b1c2429f
#840 b68ace95fae5fb05a17697a667f69b020630d136 0cbcc5411190f48a955b0bd36aa7cd4aaeacf954

Mechanical conflict resolutions: #824 Cargo.lock retains calternal-imap-uid and calternal-markdown-view entries in name order. #828 execute.rs retains app-password capability checks, then starts timing immediately before runtime lookup. #827 contains 91a19dcf; #828 contains fe46caf5.

#831 measurement files moved to the preceding PR comment and removed in a separate cleanup commit.

Validation: first runs only, CI=1, no retries; Nix shell with strace; all heavy steps through ~/bin/slot. Full native Playwright project matrix split across dev-01 (non-WebKit) and dev-02 (WebKit plus admin-setup). Cross-half scheduling dependencies removed; within-half dependencies and admin setup retained. Failed cases will receive one origin/dev run at the same spec/project for attribution. No merge into dev.

Assembly complete. Head `4d02e704a281dda00655f45aca6d0d6744fb6357` (full head recorded by gates), base `55f35fcde3bc91ca3781055577b93e93819f6cd4`. All requested PRs included; none excluded. Captured current heads at assembly time: | PR | Source head | Merge | |---|---|---| | #779 | `7b97e4d14231be07f6a80cb9ca172d02442c487e` | `f8931e0d3081da14f79b5639e33d16c5591b565b` | | #858 | `9c05d871a1692df46eb9e06e0e386bdf8c0bbd9c` | `ac1a23dde8ab32f7033d5b58194b7d25caa99a57` | | #824 | `f1c1aa961e169ef99e132063a97ccbd9a89a5def` | `6e5e659044873b52d95cdca6ee6d4df765ef6a0c` | | #827 | `91a19dcf0a9cf3844861e0e6acf4318618f3ea33` | `c3a626df5418c3b5a1d4e73e4965b0bede7ab925` | | #829 | `d455842942731aabc05474c7929597d57090a7cc` | `807d3b6955df8b6e972fe897f9240cac2e0c4c85` | | #835 | `19071be8455d718822ba98f6b9a402c5fd345242` | `f8268ea5f1be46a2aef2ce04efa83c6c0adcd0fe` | | #828 | `5d7b9b36cd008de78851809724c90a85d92b40a7` | `d3f297b719c4ab851aee749d3a0e3f3737e3624a` | | #833 | `ccaece3dfecbe2048634fc792d7d4a2c6e31f324` | `b2a141ea1553ddd8c8a1de197fb3bd2aa686f60e` | | #836 | `5f7d1a59776411e7f3353d0023817b43affae51e` | `7959619d8ecf3c885d877dc3557cd52fd8eefbb6` | | #831 | `316b43e9578c3e4d2944df8df43caebc611356f7` | `110abecb403bfc6b9e8fd9654a46892c55f62931` | | #844 | `50cce9dcceb87f23a9f346300b3336e315ff6c0c` | `9abec9665d4009563edf380e4ae688c1ea68857e` | | #847 | `40448327a15730271d56226b898e48bcc87b2a4d` | `49ff52c50f6bd9e750c416924783eec7e95a1932` | | #855 | `c38c3083d51f4adcf5cd76a70f1f6960b427413e` | `00b4107c2d713204d8b09ac5f4ed54ec77077aad` | | #857 | `144e717145e35f70356f593b7c04a527e00f7b17` | `02e53ae075e78e4b49be7881459a7ab59f4097ce` | | #856 | `85845e3d11bea351608bde7481779ba2303372e5` | `09bd556a905eac10bb1e4232655220f1b1c2429f` | | #840 | `b68ace95fae5fb05a17697a667f69b020630d136` | `0cbcc5411190f48a955b0bd36aa7cd4aaeacf954` | Mechanical conflict resolutions: #824 Cargo.lock retains calternal-imap-uid and calternal-markdown-view entries in name order. #828 execute.rs retains app-password capability checks, then starts timing immediately before runtime lookup. #827 contains 91a19dcf; #828 contains fe46caf5. #831 measurement files moved to the preceding PR comment and removed in a separate cleanup commit. Validation: first runs only, CI=1, no retries; Nix shell with strace; all heavy steps through ~/bin/slot. Full native Playwright project matrix split across dev-01 (non-WebKit) and dev-02 (WebKit plus admin-setup). Cross-half scheduling dependencies removed; within-half dependencies and admin setup retained. Failed cases will receive one origin/dev run at the same spec/project for attribution. No merge into dev.
Author
Owner

Harness setup note: the initial detached launch on both VMs inherited the home directory, and Nix exited before entering the shell: path "/home/{claude,cua}" does not contain a flake.nix ... could not find a flake.nix file. No install, build, gate or test executed. Corrected with an explicit cd ~/Developer/Worktrees/bm8 inside the detached command; first executed gate runs remain authoritative.

Harness setup note: the initial detached launch on both VMs inherited the home directory, and Nix exited before entering the shell: `path "/home/{claude,cua}" does not contain a flake.nix ... could not find a flake.nix file`. No install, build, gate or test executed. Corrected with an explicit `cd ~/Developer/Worktrees/bm8` inside the detached command; first executed gate runs remain authoritative.
Author
Owner

First executed gates so far: CI=1 cargo fmt --all --check PASS; CI=1 cargo clippy --workspace --all-targets --locked -j4 -- -D warnings PASS. cargo test --workspace -j4 is compiling/running. Dev-02 browser prerequisites cargo build -j4 and bun run build both PASS. Train head: 4d02e704a281dda00655f45aca6d0d6744fb6357. Nix resolves strace to /nix/store/jv2wdzlnwcmizjn0y0mz80w17klicz0g-strace-7.2/bin/strace on dev-02. Origin/dev attribution baseline is 55f35fcde3bc91ca3781055577b93e93819f6cd4, with only #779 flake tooling overlaid and uncommitted, no implementation changes.

First executed gates so far: `CI=1 cargo fmt --all --check` PASS; `CI=1 cargo clippy --workspace --all-targets --locked -j4 -- -D warnings` PASS. `cargo test --workspace -j4` is compiling/running. Dev-02 browser prerequisites `cargo build -j4` and `bun run build` both PASS. Train head: `4d02e704a281dda00655f45aca6d0d6744fb6357`. Nix resolves strace to `/nix/store/jv2wdzlnwcmizjn0y0mz80w17klicz0g-strace-7.2/bin/strace` on dev-02. Origin/dev attribution baseline is `55f35fcde3bc91ca3781055577b93e93819f6cd4`, with only #779 flake tooling overlaid and uncommitted, no implementation changes.
Author
Owner

First-run gate results on 4d02e704a281dda00655f45aca6d0d6744fb6357 (CI=1, Nix shell with strace, heavy steps through ~/bin/slot):

Command Result
cargo fmt --all --check PASS
cargo clippy --workspace --all-targets --locked -j4 -- -D warnings PASS
cargo test --workspace -j4 PASS, complete workspace run including doc tests
cargo build -j4 PASS
bun run build (apps/web) PASS
PERF_DIST="$PWD/apps/web/dist" PERF_OUT="$HOME/bm8-train/bundle" PERF_WORK="$HOME/bm8-train/bundle-work" bash perf/web.sh --bundle-only --gate FAIL (1)
node ci/check-case-paths.mjs PASS
node ci/check-no-evidence.mjs FAIL (1)

Bundle first error:

over budget: web.bundle.all_js_brotli@0: p95 317 > 315

Evidence guard first error:

ADR 0003: put run artifacts in their Forgejo issue or PR, not the repository:
  apps/web/src/composer/ResultsList.svelte

ResultsList.svelte is product source from #840, so this is a new guard false positive from the broad results path pattern, not a captured artifact. No gate retries or implementation changes. Baseline checks for both failing commands are in progress. Full browser halves released together after these gates; final per-project tables and every first error will follow.

First-run gate results on `4d02e704a281dda00655f45aca6d0d6744fb6357` (`CI=1`, Nix shell with strace, heavy steps through ~/bin/slot): | Command | Result | |---|---| | `cargo fmt --all --check` | PASS | | `cargo clippy --workspace --all-targets --locked -j4 -- -D warnings` | PASS | | `cargo test --workspace -j4` | PASS, complete workspace run including doc tests | | `cargo build -j4` | PASS | | `bun run build` (apps/web) | PASS | | `PERF_DIST="$PWD/apps/web/dist" PERF_OUT="$HOME/bm8-train/bundle" PERF_WORK="$HOME/bm8-train/bundle-work" bash perf/web.sh --bundle-only --gate` | FAIL (1) | | `node ci/check-case-paths.mjs` | PASS | | `node ci/check-no-evidence.mjs` | FAIL (1) | Bundle first error: ```text over budget: web.bundle.all_js_brotli@0: p95 317 > 315 ``` Evidence guard first error: ```text ADR 0003: put run artifacts in their Forgejo issue or PR, not the repository: apps/web/src/composer/ResultsList.svelte ``` `ResultsList.svelte` is product source from #840, so this is a new guard false positive from the broad `results` path pattern, not a captured artifact. No gate retries or implementation changes. Baseline checks for both failing commands are in progress. Full browser halves released together after these gates; final per-project tables and every first error will follow.
Author
Owner

Single-run origin/dev attribution for the two gate failures (55f35fcde3bc91ca3781055577b93e93819f6cd4, CI=1, Nix + slot):

Train failure Same baseline command, once Classification
bundle web.bundle.all_js_brotli: 317 > 315 KiB PERF_DIST="$PWD/apps/web/dist" PERF_OUT="$HOME/bm8-baseline/bundle" PERF_WORK="$HOME/bm8-baseline/bundle-work" bash perf/web.sh --bundle-only --gate: exit 0 NEW regression
guard flags apps/web/src/composer/ResultsList.svelte node ci/check-no-evidence.mjs: exit 1, script absent (Cannot find module .../ci/check-no-evidence.mjs) NEW integration failure; not the same failure on baseline

No baseline gate was retried. The guard and the flagged product source are introduced by this train. The false positive needs a source-path allowance or narrower policy; no implementation choice was made in the train.

Browser release: WebKit/dev-02 2026-11-02 19:10:18 IST; Chromium/dev-01 19:10:19 IST. Disk checks at release: 335 GB and 310 GB available, respectively. Native project selection: 2,134 Chromium-half cases and 2,113 WebKit-half cases, including skips; admin-setup included in each invocation. --workers=1 --retries=0, no retry.

Single-run origin/dev attribution for the two gate failures (`55f35fcde3bc91ca3781055577b93e93819f6cd4`, CI=1, Nix + slot): | Train failure | Same baseline command, once | Classification | |---|---|---| | bundle `web.bundle.all_js_brotli`: 317 > 315 KiB | `PERF_DIST="$PWD/apps/web/dist" PERF_OUT="$HOME/bm8-baseline/bundle" PERF_WORK="$HOME/bm8-baseline/bundle-work" bash perf/web.sh --bundle-only --gate`: exit 0 | NEW regression | | guard flags `apps/web/src/composer/ResultsList.svelte` | `node ci/check-no-evidence.mjs`: exit 1, script absent (`Cannot find module .../ci/check-no-evidence.mjs`) | NEW integration failure; not the same failure on baseline | No baseline gate was retried. The guard and the flagged product source are introduced by this train. The false positive needs a source-path allowance or narrower policy; no implementation choice was made in the train. Browser release: WebKit/dev-02 `2026-11-02 19:10:18 IST`; Chromium/dev-01 `19:10:19 IST`. Disk checks at release: 335 GB and 310 GB available, respectively. Native project selection: 2,134 Chromium-half cases and 2,113 WebKit-half cases, including skips; admin-setup included in each invocation. `--workers=1 --retries=0`, no retry.
Author
Owner

First browser error, recorded while the full suite continues. Attribution pending one origin/dev run; no train retry.

1) [chromium-1440] › e2e/search-corpus.spec.ts:44:1 › every prefix, the full word, one-letter and two-letter typos find their note 

    Error: missed: prefix "ma" -> marmalade (rank 5); 1 typo "mrmalade" -> marmalade (rank -1); 1 typo "amrmalade" -> marmalade (rank -1); 1 typo "mermalade" -> marmalade (rank -1); 1 typo "mxarmalade" -> marmalade (rank -1); 1 typo "mamalade" -> marmalade (rank -1); 1 typo "mramalade" -> marmalade (rank -1); 1 typo "maemalade" -> marmalade (rank -1); 1 typo "maxrmalade" -> marmalade (rank -1); 1 typo "maralade" -> marmalade (rank -1); 1 typo "mamralade" -> marmalade (rank -1); 1 typo "marealade" -> marmalade (rank -1)

    expect(received).toBeLessThanOrEqual(expected)

    Expected: <= 0.02
    Received:    0.7151474530831099

      110 |   console.log(`search corpus server time (took_ms), limit ${LIMIT}: ${served.length} queries, p50 ${q(0.5)} ms, p95 ${q(0.95)} ms, p99 ${q(0.99)} ms`)
      111 |   if (misses.length) console.log(misses.slice(0, 40).join('\n'))
    > 112 |   expect(misses.length / asked, `missed: ${misses.slice(0, 12).join('; ')}`).toBeLessThanOrEqual(0.02)
          |                                                                              ^
      113 |   } finally { await clearCorpus() }
      114 |   // Latency is not asserted here: this goes through whatever sits between the test and the server (the Studio's
      115 |   // tunnel adds ~150 ms). The server's own numbers come from the perf gate (perf/budgets.json, search.query).
        at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/search-corpus.spec.ts:112:78


First browser error, recorded while the full suite continues. Attribution pending one origin/dev run; no train retry. ```text 1) [chromium-1440] › e2e/search-corpus.spec.ts:44:1 › every prefix, the full word, one-letter and two-letter typos find their note Error: missed: prefix "ma" -> marmalade (rank 5); 1 typo "mrmalade" -> marmalade (rank -1); 1 typo "amrmalade" -> marmalade (rank -1); 1 typo "mermalade" -> marmalade (rank -1); 1 typo "mxarmalade" -> marmalade (rank -1); 1 typo "mamalade" -> marmalade (rank -1); 1 typo "mramalade" -> marmalade (rank -1); 1 typo "maemalade" -> marmalade (rank -1); 1 typo "maxrmalade" -> marmalade (rank -1); 1 typo "maralade" -> marmalade (rank -1); 1 typo "mamralade" -> marmalade (rank -1); 1 typo "marealade" -> marmalade (rank -1) expect(received).toBeLessThanOrEqual(expected) Expected: <= 0.02 Received: 0.7151474530831099 110 | console.log(`search corpus server time (took_ms), limit ${LIMIT}: ${served.length} queries, p50 ${q(0.5)} ms, p95 ${q(0.95)} ms, p99 ${q(0.99)} ms`) 111 | if (misses.length) console.log(misses.slice(0, 40).join('\n')) > 112 | expect(misses.length / asked, `missed: ${misses.slice(0, 12).join('; ')}`).toBeLessThanOrEqual(0.02) | ^ 113 | } finally { await clearCorpus() } 114 | // Latency is not asserted here: this goes through whatever sits between the test and the server (the Studio's 115 | // tunnel adds ~150 ms). The server's own numbers come from the perf gate (perf/budgets.json, search.query). at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/search-corpus.spec.ts:112:78 ```
Author
Owner

Origin/dev one-run attribution: CI=1 CALTERNAL_BIN="$PWD/../../target/debug/calternal" E2E_DIST="$PWD/dist" bunx playwright test e2e/search-corpus.spec.ts --project=chromium-1440 --workers=1 --retries=0 --reporter=line,json on dev-01, baseline 55f35fcde3bc91ca3781055577b93e93819f6cd4: 2 passed, exit 0; recall case reported 1,492 queries, 0 missed. Therefore the train chromium-1440 recall failure (miss fraction 0.7151474530831099 > 0.02) is not reproduced on origin/dev, and is classified new/unresolved, not pre-existing. This spec/project baseline will not be rerun. Full train halves continue.

Origin/dev one-run attribution: `CI=1 CALTERNAL_BIN="$PWD/../../target/debug/calternal" E2E_DIST="$PWD/dist" bunx playwright test e2e/search-corpus.spec.ts --project=chromium-1440 --workers=1 --retries=0 --reporter=line,json` on dev-01, baseline `55f35fcde3bc91ca3781055577b93e93819f6cd4`: **2 passed**, exit 0; recall case reported 1,492 queries, 0 missed. Therefore the train chromium-1440 recall failure (miss fraction 0.7151474530831099 > 0.02) is **not reproduced on origin/dev**, and is classified new/unresolved, not pre-existing. This spec/project baseline will not be rerun. Full train halves continue.
Author
Owner

Origin/dev one-run attribution for both WebKit preload failures: CI=1 CALTERNAL_BIN="$PWD/../../target/debug/calternal" E2E_DIST="$PWD/dist" bunx playwright test e2e/preload.spec.ts --project=webkit-1440 --workers=1 --retries=0 --reporter=line,json on dev-02: 9 passed, 3 skipped, exit 0. Both Control and Meta intent cases passed. The train failures are new/unresolved, not reproduced on baseline; this spec/project baseline will not be rerun. Full first errors will also be included in the final catalog.

Origin/dev one-run attribution for both WebKit preload failures: `CI=1 CALTERNAL_BIN="$PWD/../../target/debug/calternal" E2E_DIST="$PWD/dist" bunx playwright test e2e/preload.spec.ts --project=webkit-1440 --workers=1 --retries=0 --reporter=line,json` on dev-02: **9 passed, 3 skipped**, exit 0. Both Control and Meta intent cases passed. The train failures are new/unresolved, not reproduced on baseline; this spec/project baseline will not be rerun. Full first errors will also be included in the final catalog.
Author
Owner

Single-run origin/dev attribution completed for the next two failures (CI=1, native config, same project/spec, --workers=1 --retries=0, no repeats):

Train failure Baseline command in apps/web Origin/dev result Classification
chromium-1440, vim.spec.ts, / searches in normal mode, in the bar; in insert mode it is a slash; :noh left 3 .cm-searchMatch elements, expected 0 bunx playwright test e2e/vim.spec.ts --project=chromium-1440 --workers=1 --retries=0 --reporter=line,json 13 passed, 1 skipped; matching case PASS new/unresolved
webkit-1440, smart-note.spec.ts, log-line note Undo; expected daily-note URL, remained on created note bunx playwright test e2e/smart-note.spec.ts --project=webkit-1440 --workers=1 --retries=0 --reporter=line,json 1 passed; matching case PASS new/unresolved

Both used the built origin/dev binary and origin/dev dist at 55f35fcde3bc91ca3781055577b93e93819f6cd4, with only Nix tooling overlay. No current browser failure has reproduced on baseline so far. Complete first-error catalogs will follow after the full halves finish.

Single-run origin/dev attribution completed for the next two failures (CI=1, native config, same project/spec, --workers=1 --retries=0, no repeats): | Train failure | Baseline command in apps/web | Origin/dev result | Classification | |---|---|---|---| | chromium-1440, `vim.spec.ts`, `/ searches in normal mode, in the bar; in insert mode it is a slash`; `:noh` left 3 `.cm-searchMatch` elements, expected 0 | `bunx playwright test e2e/vim.spec.ts --project=chromium-1440 --workers=1 --retries=0 --reporter=line,json` | 13 passed, 1 skipped; matching case PASS | new/unresolved | | webkit-1440, `smart-note.spec.ts`, log-line note Undo; expected daily-note URL, remained on created note | `bunx playwright test e2e/smart-note.spec.ts --project=webkit-1440 --workers=1 --retries=0 --reporter=line,json` | 1 passed; matching case PASS | new/unresolved | Both used the built origin/dev binary and origin/dev dist at `55f35fcde3bc91ca3781055577b93e93819f6cd4`, with only Nix tooling overlay. No current browser failure has reproduced on baseline so far. Complete first-error catalogs will follow after the full halves finish.
Author
Owner

Single-run origin/dev attribution for Chromium phone failures (CI=1, native config, same project/spec, --workers=1 --retries=0):

Train failure Baseline command in apps/web Origin/dev result Classification
chromium-390 glass.spec.ts:341, search surface shadow differs from the bar shadow bunx playwright test e2e/glass.spec.ts --project=chromium-390 --workers=1 --retries=0 --reporter=line,json 4 passed, 2 skipped; matching case PASS new/unresolved
chromium-390 lazy-styled.spec.ts:54, search first-frame font weights change (620↔500) bunx playwright test e2e/lazy-styled.spec.ts --project=chromium-390 --workers=1 --retries=0 --reporter=line,json 3 passed, 1 skipped; matching case PASS new/unresolved

Baseline head 55f35fcde3bc91ca3781055577b93e93819f6cd4; no baseline spec/project repeated. These are functional rendering assertions, not frame-performance measurements.

Single-run origin/dev attribution for Chromium phone failures (CI=1, native config, same project/spec, --workers=1 --retries=0): | Train failure | Baseline command in apps/web | Origin/dev result | Classification | |---|---|---|---| | chromium-390 `glass.spec.ts:341`, search surface shadow differs from the bar shadow | `bunx playwright test e2e/glass.spec.ts --project=chromium-390 --workers=1 --retries=0 --reporter=line,json` | 4 passed, 2 skipped; matching case PASS | new/unresolved | | chromium-390 `lazy-styled.spec.ts:54`, search first-frame font weights change (620↔500) | `bunx playwright test e2e/lazy-styled.spec.ts --project=chromium-390 --workers=1 --retries=0 --reporter=line,json` | 3 passed, 1 skipped; matching case PASS | new/unresolved | Baseline head `55f35fcde3bc91ca3781055577b93e93819f6cd4`; no baseline spec/project repeated. These are functional rendering assertions, not frame-performance measurements.
Author
Owner

Chromium interim first-error catalog, head 4d02e704a281dda00655f45aca6d0d6744fb6357. Full suite is still running. No retries. Every captured block below is the first attempt.

1) [chromium-1440] › e2e/search-corpus.spec.ts:44:1 › every prefix, the full word, one-letter and two-letter typos find their note 

    Error: missed: prefix "ma" -> marmalade (rank 5); 1 typo "mrmalade" -> marmalade (rank -1); 1 typo "amrmalade" -> marmalade (rank -1); 1 typo "mermalade" -> marmalade (rank -1); 1 typo "mxarmalade" -> marmalade (rank -1); 1 typo "mamalade" -> marmalade (rank -1); 1 typo "mramalade" -> marmalade (rank -1); 1 typo "maemalade" -> marmalade (rank -1); 1 typo "maxrmalade" -> marmalade (rank -1); 1 typo "maralade" -> marmalade (rank -1); 1 typo "mamralade" -> marmalade (rank -1); 1 typo "marealade" -> marmalade (rank -1)

    expect(received).toBeLessThanOrEqual(expected)

    Expected: <= 0.02
    Received:    0.7151474530831099

      110 |   console.log(`search corpus server time (took_ms), limit ${LIMIT}: ${served.length} queries, p50 ${q(0.5)} ms, p95 ${q(0.95)} ms, p99 ${q(0.99)} ms`)
      111 |   if (misses.length) console.log(misses.slice(0, 40).join('\n'))
    > 112 |   expect(misses.length / asked, `missed: ${misses.slice(0, 12).join('; ')}`).toBeLessThanOrEqual(0.02)
          |                                                                              ^
      113 |   } finally { await clearCorpus() }
      114 |   // Latency is not asserted here: this goes through whatever sits between the test and the server (the Studio's
      115 |   // tunnel adds ~150 ms). The server's own numbers come from the perf gate (perf/budgets.json, search.query).
        at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/search-corpus.spec.ts:112:78


2) [chromium-1440] › e2e/vim.spec.ts:341:3 › Vim mode › / searches in normal mode, in the bar; in insert mode it is a slash 

    Error: expect(locator).toHaveCount(expected) failed

    Locator:  locator('.cm-searchMatch')
    Expected: 0
    Received: 3
    Timeout:  5000ms

    Call log:
      - Expect "toHaveCount" locator('.cm-searchMatch') with timeout 5000ms
      - waiting for locator('.cm-searchMatch')
        14 × locator resolved to 3 elements
           - unexpected value "3"


      370 |     await typeKeys(page, 'noh')
      371 |     await keys(page, 'Enter')
    > 372 |     await expect(page.locator('.cm-searchMatch')).toHaveCount(0)
          |                                                   ^
      373 |     // Insert mode: `/` is just a slash.
      374 |     await keys(page, 'A')
      375 |     await typeKeys(page, ' a/b')
        at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/vim.spec.ts:372:51


3) [chromium-390] › e2e/glass.spec.ts:341:1 › every floating surface is the one glass ────────────

    Error: search: the bar's shade, edge and shadow

    expect(received).toBe(expected) // Object.is equality

    Expected: "rgba(16, 15, 14, 0.3) 0px 0px 20px 0px inset, rgba(255, 255, 255, 0.1) 0px 0px 0px 1px inset, rgba(255, 255, 255, 0.14) 0px 1px 0px 0px inset, rgba(255, 255, 255, 0.08) 0px 0px 0px 1px, rgba(0, 0, 0, 0.55) 0px 12px 30px -10px"
    Received: "rgba(16, 15, 14, 0.3) 0px 0px 20px 0px inset, rgba(255, 255, 255, 0.1) 0px 0px 0px 1px inset, rgba(255, 255, 255, 0.14) 0px 1px 0px 0px inset"

      369 |   const same = (m: typeof bar, dense: boolean, what: string) => {
      370 |     expect(m.filter, `${what}: the bar's blur`).toBe(bar.filter)
    > 371 |     expect(m.shadow, `${what}: the bar's shade, edge and shadow`).toBe(bar.shadow)
          |                                                                   ^
      372 |     expect(m.kids, `${what}: its glass holds nothing`).toBe(0)
      373 |     return tint(dense).then((t) => expect(m.bg, `${what}: its tint`).toBe(t))
      374 |   }
        at same (/home/claude/Developer/Worktrees/bm8/apps/web/e2e/glass.spec.ts:371:67)
        at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/glass.spec.ts:379:9


4) [chromium-390] › e2e/lazy-styled.spec.ts:54:1 › lazily loaded sheets, search and menu are styled on their first frame 

    Error: search: unstyled on its first frame

    expect(received).toEqual(expected) // deep equality

    - Expected  - 1
    + Received  + 4

    - Array []
    + Array [
    +   "BUTTON.kopt press svelte-3pqrsc fontWeight: 620 -> 500",
    +   "BUTTON.kopt press svelte-3pqrsc fontWeight: 500 -> 620",
    + ]

      82 |   const search = await flashes(page, '.palette')
      83 |   expect(search.seen).toBeGreaterThan(3)
    > 84 |   expect(search.bad, 'search: unstyled on its first frame').toEqual([])
         |                                                             ^
      85 |   await page.keyboard.press('Escape')
      86 |
      87 |   // The shared menu also loads its styles before its first open.
        at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/lazy-styled.spec.ts:84:61


All currently listed cases passed their same-spec/project single origin/dev runs. They are new/unresolved, not classified pre-existing. Final JSON project totals and any further first errors will follow.

Chromium interim first-error catalog, head `4d02e704a281dda00655f45aca6d0d6744fb6357`. Full suite is still running. No retries. Every captured block below is the first attempt. ```text 1) [chromium-1440] › e2e/search-corpus.spec.ts:44:1 › every prefix, the full word, one-letter and two-letter typos find their note Error: missed: prefix "ma" -> marmalade (rank 5); 1 typo "mrmalade" -> marmalade (rank -1); 1 typo "amrmalade" -> marmalade (rank -1); 1 typo "mermalade" -> marmalade (rank -1); 1 typo "mxarmalade" -> marmalade (rank -1); 1 typo "mamalade" -> marmalade (rank -1); 1 typo "mramalade" -> marmalade (rank -1); 1 typo "maemalade" -> marmalade (rank -1); 1 typo "maxrmalade" -> marmalade (rank -1); 1 typo "maralade" -> marmalade (rank -1); 1 typo "mamralade" -> marmalade (rank -1); 1 typo "marealade" -> marmalade (rank -1) expect(received).toBeLessThanOrEqual(expected) Expected: <= 0.02 Received: 0.7151474530831099 110 | console.log(`search corpus server time (took_ms), limit ${LIMIT}: ${served.length} queries, p50 ${q(0.5)} ms, p95 ${q(0.95)} ms, p99 ${q(0.99)} ms`) 111 | if (misses.length) console.log(misses.slice(0, 40).join('\n')) > 112 | expect(misses.length / asked, `missed: ${misses.slice(0, 12).join('; ')}`).toBeLessThanOrEqual(0.02) | ^ 113 | } finally { await clearCorpus() } 114 | // Latency is not asserted here: this goes through whatever sits between the test and the server (the Studio's 115 | // tunnel adds ~150 ms). The server's own numbers come from the perf gate (perf/budgets.json, search.query). at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/search-corpus.spec.ts:112:78 2) [chromium-1440] › e2e/vim.spec.ts:341:3 › Vim mode › / searches in normal mode, in the bar; in insert mode it is a slash Error: expect(locator).toHaveCount(expected) failed Locator: locator('.cm-searchMatch') Expected: 0 Received: 3 Timeout: 5000ms Call log: - Expect "toHaveCount" locator('.cm-searchMatch') with timeout 5000ms - waiting for locator('.cm-searchMatch') 14 × locator resolved to 3 elements - unexpected value "3" 370 | await typeKeys(page, 'noh') 371 | await keys(page, 'Enter') > 372 | await expect(page.locator('.cm-searchMatch')).toHaveCount(0) | ^ 373 | // Insert mode: `/` is just a slash. 374 | await keys(page, 'A') 375 | await typeKeys(page, ' a/b') at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/vim.spec.ts:372:51 3) [chromium-390] › e2e/glass.spec.ts:341:1 › every floating surface is the one glass ──────────── Error: search: the bar's shade, edge and shadow expect(received).toBe(expected) // Object.is equality Expected: "rgba(16, 15, 14, 0.3) 0px 0px 20px 0px inset, rgba(255, 255, 255, 0.1) 0px 0px 0px 1px inset, rgba(255, 255, 255, 0.14) 0px 1px 0px 0px inset, rgba(255, 255, 255, 0.08) 0px 0px 0px 1px, rgba(0, 0, 0, 0.55) 0px 12px 30px -10px" Received: "rgba(16, 15, 14, 0.3) 0px 0px 20px 0px inset, rgba(255, 255, 255, 0.1) 0px 0px 0px 1px inset, rgba(255, 255, 255, 0.14) 0px 1px 0px 0px inset" 369 | const same = (m: typeof bar, dense: boolean, what: string) => { 370 | expect(m.filter, `${what}: the bar's blur`).toBe(bar.filter) > 371 | expect(m.shadow, `${what}: the bar's shade, edge and shadow`).toBe(bar.shadow) | ^ 372 | expect(m.kids, `${what}: its glass holds nothing`).toBe(0) 373 | return tint(dense).then((t) => expect(m.bg, `${what}: its tint`).toBe(t)) 374 | } at same (/home/claude/Developer/Worktrees/bm8/apps/web/e2e/glass.spec.ts:371:67) at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/glass.spec.ts:379:9 4) [chromium-390] › e2e/lazy-styled.spec.ts:54:1 › lazily loaded sheets, search and menu are styled on their first frame Error: search: unstyled on its first frame expect(received).toEqual(expected) // deep equality - Expected - 1 + Received + 4 - Array [] + Array [ + "BUTTON.kopt press svelte-3pqrsc fontWeight: 620 -> 500", + "BUTTON.kopt press svelte-3pqrsc fontWeight: 500 -> 620", + ] 82 | const search = await flashes(page, '.palette') 83 | expect(search.seen).toBeGreaterThan(3) > 84 | expect(search.bad, 'search: unstyled on its first frame').toEqual([]) | ^ 85 | await page.keyboard.press('Escape') 86 | 87 | // The shared menu also loads its styles before its first open. at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/lazy-styled.spec.ts:84:61 ``` All currently listed cases passed their same-spec/project single origin/dev runs. They are new/unresolved, not classified pre-existing. Final JSON project totals and any further first errors will follow.
Author
Owner

Webkit interim first-error catalog, head 4d02e704a281dda00655f45aca6d0d6744fb6357. Full suite is still running. No retries. Every captured block below is the first attempt.

1) [webkit-1440] › e2e/preload.spec.ts:111:3 › palette Control intent prepares only its own screen before idle 

    Error: opening the prepared palette needs no new scripts

    expect(received).toHaveLength(expected)

    Expected length: 4
    Received length: 5
    Received array:  ["/assets/index-BR4nh5B0.js", "/assets/rolldown-runtime-BzPIFoFC.js", "/assets/two-finger-CkCPX5CV.js", "/assets/Palette-Duz6PTlQ.js", "/assets/index-BR4nh5B0.js"]

      132 |     await page.keyboard.press('Control+k')
      133 |     await expect(page.locator('.palette [role="combobox"]')).toBeVisible()
    > 134 |     expect(scripts, 'opening the prepared palette needs no new scripts').toHaveLength(ready)
          |                                                                          ^
      135 |   })
      136 | }
      137 |
        at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/preload.spec.ts:134:74


2) [webkit-1440] › e2e/preload.spec.ts:111:3 › palette Meta intent prepares only its own screen before idle 

    Error: opening the prepared palette needs no new scripts

    expect(received).toHaveLength(expected)

    Expected length: 4
    Received length: 5
    Received array:  ["/assets/index-BR4nh5B0.js", "/assets/rolldown-runtime-BzPIFoFC.js", "/assets/two-finger-CkCPX5CV.js", "/assets/Palette-Duz6PTlQ.js", "/assets/index-BR4nh5B0.js"]

      132 |     await page.keyboard.press('Control+k')
      133 |     await expect(page.locator('.palette [role="combobox"]')).toBeVisible()
    > 134 |     expect(scripts, 'opening the prepared palette needs no new scripts').toHaveLength(ready)
          |                                                                          ^
      135 |   })
      136 | }
      137 |
        at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/preload.spec.ts:134:74


3) [webkit-1440] › e2e/smart-note.spec.ts:19:1 › → on a log line makes a note with its times and tags, and Undo takes it back 

    Error: expect(page).toHaveURL(expected) failed

    Expected pattern: /day-2068-12-10/
    Received string:  "http://127.0.0.1:35159/?test=1#/note/01a19c91-f06d-7014-a0b7-db00b4556a65?list=journal"
    Timeout: 5000ms

    Call log:
      - Expect "toHaveURL" with timeout 5000ms
        14 × locator resolved to <html class="" lang="en" data-sky="dusk" data-app="ready" data-ready="true" data-scheme="dark" data-listed="true" data-synced="true" data-backdrop="sky" data-input="pointer" data-accent="marigold" data-last-input="pointer">…</html>
           - unexpected value "http://127.0.0.1:35159/?test=1#/note/01a19c91-f06d-7014-a0b7-db00b4556a65?list=journal"


      52 |   // One Undo: the note goes and the line comes back exactly.
      53 |   await page.getByRole('button', { name: 'Undo' }).click()
    > 54 |   await expect(page).toHaveURL(new RegExp(`day-${iso}`))
         |                      ^
      55 |   await expect.poll(async () => body((await get(`day-${iso}`)).text), { timeout: 15_000 }).toBe(before)
      56 |   // (The editor shows the bullet as a dot.)
      57 |   await expect(page.getByRole('textbox', { name: 'Note text' })).toContainText(`09:30 - 10:30 ${words} #area/abc`)
        at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/smart-note.spec.ts:54:22


4) [webkit-390] › e2e/glass.spec.ts:341:1 › every floating surface is the one glass ──────────────

    Error: search: the bar's shade, edge and shadow

    expect(received).toBe(expected) // Object.is equality

    Expected: "rgba(16, 15, 14, 0.3) 0px 0px 20px 0px inset, rgba(255, 255, 255, 0.1) 0px 0px 0px 1px inset, rgba(255, 255, 255, 0.14) 0px 1px 0px 0px inset, rgba(255, 255, 255, 0.08) 0px 0px 0px 1px, rgba(0, 0, 0, 0.55) 0px 12px 30px -10px"
    Received: "rgba(16, 15, 14, 0.3) 0px 0px 20px 0px inset, rgba(255, 255, 255, 0.1) 0px 0px 0px 1px inset, rgba(255, 255, 255, 0.14) 0px 1px 0px 0px inset"

      369 |   const same = (m: typeof bar, dense: boolean, what: string) => {
      370 |     expect(m.filter, `${what}: the bar's blur`).toBe(bar.filter)
    > 371 |     expect(m.shadow, `${what}: the bar's shade, edge and shadow`).toBe(bar.shadow)
          |                                                                   ^
      372 |     expect(m.kids, `${what}: its glass holds nothing`).toBe(0)
      373 |     return tint(dense).then((t) => expect(m.bg, `${what}: its tint`).toBe(t))
      374 |   }
        at same (/home/cua/Developer/Worktrees/bm8/apps/web/e2e/glass.spec.ts:371:67)
        at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/glass.spec.ts:379:9


5) [webkit-390] › e2e/lazy-styled.spec.ts:54:1 › lazily loaded sheets, search and menu are styled on their first frame 

    Error: search: unstyled on its first frame

    expect(received).toEqual(expected) // deep equality

    - Expected  - 1
    + Received  + 4

    - Array []
    + Array [
    +   "BUTTON.kopt press svelte-3pqrsc fontWeight: 620 -> 500",
    +   "BUTTON.kopt press svelte-3pqrsc fontWeight: 500 -> 620",
    + ]

      82 |   const search = await flashes(page, '.palette')
      83 |   expect(search.seen).toBeGreaterThan(3)
    > 84 |   expect(search.bad, 'search: unstyled on its first frame').toEqual([])
         |                                                             ^
      85 |   await page.keyboard.press('Escape')
      86 |
      87 |   // The shared menu also loads its styles before its first open.
        at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/lazy-styled.spec.ts:84:61


All currently listed cases passed their same-spec/project single origin/dev runs. They are new/unresolved, not classified pre-existing. Final JSON project totals and any further first errors will follow.

Webkit interim first-error catalog, head `4d02e704a281dda00655f45aca6d0d6744fb6357`. Full suite is still running. No retries. Every captured block below is the first attempt. ```text 1) [webkit-1440] › e2e/preload.spec.ts:111:3 › palette Control intent prepares only its own screen before idle Error: opening the prepared palette needs no new scripts expect(received).toHaveLength(expected) Expected length: 4 Received length: 5 Received array: ["/assets/index-BR4nh5B0.js", "/assets/rolldown-runtime-BzPIFoFC.js", "/assets/two-finger-CkCPX5CV.js", "/assets/Palette-Duz6PTlQ.js", "/assets/index-BR4nh5B0.js"] 132 | await page.keyboard.press('Control+k') 133 | await expect(page.locator('.palette [role="combobox"]')).toBeVisible() > 134 | expect(scripts, 'opening the prepared palette needs no new scripts').toHaveLength(ready) | ^ 135 | }) 136 | } 137 | at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/preload.spec.ts:134:74 2) [webkit-1440] › e2e/preload.spec.ts:111:3 › palette Meta intent prepares only its own screen before idle Error: opening the prepared palette needs no new scripts expect(received).toHaveLength(expected) Expected length: 4 Received length: 5 Received array: ["/assets/index-BR4nh5B0.js", "/assets/rolldown-runtime-BzPIFoFC.js", "/assets/two-finger-CkCPX5CV.js", "/assets/Palette-Duz6PTlQ.js", "/assets/index-BR4nh5B0.js"] 132 | await page.keyboard.press('Control+k') 133 | await expect(page.locator('.palette [role="combobox"]')).toBeVisible() > 134 | expect(scripts, 'opening the prepared palette needs no new scripts').toHaveLength(ready) | ^ 135 | }) 136 | } 137 | at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/preload.spec.ts:134:74 3) [webkit-1440] › e2e/smart-note.spec.ts:19:1 › → on a log line makes a note with its times and tags, and Undo takes it back Error: expect(page).toHaveURL(expected) failed Expected pattern: /day-2068-12-10/ Received string: "http://127.0.0.1:35159/?test=1#/note/01a19c91-f06d-7014-a0b7-db00b4556a65?list=journal" Timeout: 5000ms Call log: - Expect "toHaveURL" with timeout 5000ms 14 × locator resolved to <html class="" lang="en" data-sky="dusk" data-app="ready" data-ready="true" data-scheme="dark" data-listed="true" data-synced="true" data-backdrop="sky" data-input="pointer" data-accent="marigold" data-last-input="pointer">…</html> - unexpected value "http://127.0.0.1:35159/?test=1#/note/01a19c91-f06d-7014-a0b7-db00b4556a65?list=journal" 52 | // One Undo: the note goes and the line comes back exactly. 53 | await page.getByRole('button', { name: 'Undo' }).click() > 54 | await expect(page).toHaveURL(new RegExp(`day-${iso}`)) | ^ 55 | await expect.poll(async () => body((await get(`day-${iso}`)).text), { timeout: 15_000 }).toBe(before) 56 | // (The editor shows the bullet as a dot.) 57 | await expect(page.getByRole('textbox', { name: 'Note text' })).toContainText(`09:30 - 10:30 ${words} #area/abc`) at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/smart-note.spec.ts:54:22 4) [webkit-390] › e2e/glass.spec.ts:341:1 › every floating surface is the one glass ────────────── Error: search: the bar's shade, edge and shadow expect(received).toBe(expected) // Object.is equality Expected: "rgba(16, 15, 14, 0.3) 0px 0px 20px 0px inset, rgba(255, 255, 255, 0.1) 0px 0px 0px 1px inset, rgba(255, 255, 255, 0.14) 0px 1px 0px 0px inset, rgba(255, 255, 255, 0.08) 0px 0px 0px 1px, rgba(0, 0, 0, 0.55) 0px 12px 30px -10px" Received: "rgba(16, 15, 14, 0.3) 0px 0px 20px 0px inset, rgba(255, 255, 255, 0.1) 0px 0px 0px 1px inset, rgba(255, 255, 255, 0.14) 0px 1px 0px 0px inset" 369 | const same = (m: typeof bar, dense: boolean, what: string) => { 370 | expect(m.filter, `${what}: the bar's blur`).toBe(bar.filter) > 371 | expect(m.shadow, `${what}: the bar's shade, edge and shadow`).toBe(bar.shadow) | ^ 372 | expect(m.kids, `${what}: its glass holds nothing`).toBe(0) 373 | return tint(dense).then((t) => expect(m.bg, `${what}: its tint`).toBe(t)) 374 | } at same (/home/cua/Developer/Worktrees/bm8/apps/web/e2e/glass.spec.ts:371:67) at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/glass.spec.ts:379:9 5) [webkit-390] › e2e/lazy-styled.spec.ts:54:1 › lazily loaded sheets, search and menu are styled on their first frame Error: search: unstyled on its first frame expect(received).toEqual(expected) // deep equality - Expected - 1 + Received + 4 - Array [] + Array [ + "BUTTON.kopt press svelte-3pqrsc fontWeight: 620 -> 500", + "BUTTON.kopt press svelte-3pqrsc fontWeight: 500 -> 620", + ] 82 | const search = await flashes(page, '.palette') 83 | expect(search.seen).toBeGreaterThan(3) > 84 | expect(search.bad, 'search: unstyled on its first frame').toEqual([]) | ^ 85 | await page.keyboard.press('Escape') 86 | 87 | // The shared menu also loads its styles before its first open. at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/lazy-styled.spec.ts:84:61 ``` All currently listed cases passed their same-spec/project single origin/dev runs. They are new/unresolved, not classified pre-existing. Final JSON project totals and any further first errors will follow.
Author
Owner

Chromium/dev-01 full first run is complete on 4d02e704a281dda00655f45aca6d0d6744fb6357: 1,338 passed, 8 failed, 781 skipped, 7 did not run, exit 1. No flaky results, no retries, no global reporter errors; maximum attempts per test is 1.

Project Passed Failed Skipped Did not run
chromium-1440 424 2 602 0
chromium-390 864 3 161 0
throttled-1440 0 0 0 2
throttled-390 0 0 0 2
admin-chromium-1440 12 0 9 0
admin-chromium-390 12 0 9 0
flow 1 0 0 0
passkeys 1 0 0 0
admin-setup 1 0 0 0
prefs-chromium 7 0 0 0
look-chromium-1440 5 0 0 0
look-chromium-390 5 0 0 0
contrast 6 3 0 0
sidebar-band-chromium 0 0 0 3

The four throttled cases were dependency-blocked by main project failures. The three sidebar-band cases were dependency-blocked by contrast failures. They were not rerun. Admin setup passed in this same invocation. Five main-project failures all passed their single origin/dev checks; the three palette contrast cases await the currently running one baseline spec invocation. WebKit remains active.

Command (apps/web): CI=1 BM8_HALF=chromium CALTERNAL_BIN="$PWD/../../target/debug/calternal" E2E_DIST="$PWD/dist" bunx playwright test --config=playwright.bm8.config.ts --output="$HOME/bm8-train/test-results" --workers=1 --retries=0 --reporter=line,json.

Chromium/dev-01 full first run is complete on `4d02e704a281dda00655f45aca6d0d6744fb6357`: **1,338 passed, 8 failed, 781 skipped, 7 did not run**, exit 1. No flaky results, no retries, no global reporter errors; maximum attempts per test is 1. | Project | Passed | Failed | Skipped | Did not run | |---|---:|---:|---:|---:| | chromium-1440 | 424 | 2 | 602 | 0 | | chromium-390 | 864 | 3 | 161 | 0 | | throttled-1440 | 0 | 0 | 0 | 2 | | throttled-390 | 0 | 0 | 0 | 2 | | admin-chromium-1440 | 12 | 0 | 9 | 0 | | admin-chromium-390 | 12 | 0 | 9 | 0 | | flow | 1 | 0 | 0 | 0 | | passkeys | 1 | 0 | 0 | 0 | | admin-setup | 1 | 0 | 0 | 0 | | prefs-chromium | 7 | 0 | 0 | 0 | | look-chromium-1440 | 5 | 0 | 0 | 0 | | look-chromium-390 | 5 | 0 | 0 | 0 | | contrast | 6 | 3 | 0 | 0 | | sidebar-band-chromium | 0 | 0 | 0 | 3 | The four throttled cases were dependency-blocked by main project failures. The three sidebar-band cases were dependency-blocked by contrast failures. They were not rerun. Admin setup passed in this same invocation. Five main-project failures all passed their single origin/dev checks; the three palette contrast cases await the currently running one baseline spec invocation. WebKit remains active. Command (apps/web): `CI=1 BM8_HALF=chromium CALTERNAL_BIN="$PWD/../../target/debug/calternal" E2E_DIST="$PWD/dist" bunx playwright test --config=playwright.bm8.config.ts --output="$HOME/bm8-train/test-results" --workers=1 --retries=0 --reporter=line,json`.
Author
Owner

Chromium/dev-01 complete first-error catalog, all 8 failures. Head 4d02e704a281dda00655f45aca6d0d6744fb6357. Every block is the first attempt, no retries.

1. chromium-390 · glass.spec.ts:341

every floating surface is the one glass

First error

Error: search: the bar's shade, edge and shadow

expect(received).toBe(expected) // Object.is equality

Expected: "rgba(16, 15, 14, 0.3) 0px 0px 20px 0px inset, rgba(255, 255, 255, 0.1) 0px 0px 0px 1px inset, rgba(255, 255, 255, 0.14) 0px 1px 0px 0px inset, rgba(255, 255, 255, 0.08) 0px 0px 0px 1px, rgba(0, 0, 0, 0.55) 0px 12px 30px -10px"
Received: "rgba(16, 15, 14, 0.3) 0px 0px 20px 0px inset, rgba(255, 255, 255, 0.1) 0px 0px 0px 1px inset, rgba(255, 255, 255, 0.14) 0px 1px 0px 0px inset"

  369 |   const same = (m: typeof bar, dense: boolean, what: string) => {
  370 |     expect(m.filter, `${what}: the bar's blur`).toBe(bar.filter)
> 371 |     expect(m.shadow, `${what}: the bar's shade, edge and shadow`).toBe(bar.shadow)
      |                                                                   ^
  372 |     expect(m.kids, `${what}: its glass holds nothing`).toBe(0)
  373 |     return tint(dense).then((t) => expect(m.bg, `${what}: its tint`).toBe(t))
  374 |   }
    at same (/home/claude/Developer/Worktrees/bm8/apps/web/e2e/glass.spec.ts:371:67)
    at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/glass.spec.ts:379:9

2. chromium-390 · lazy-styled.spec.ts:54

lazily loaded sheets, search and menu are styled on their first frame

First error

Error: search: unstyled on its first frame

expect(received).toEqual(expected) // deep equality

- Expected  - 1
+ Received  + 4

- Array []
+ Array [
+   "BUTTON.kopt press svelte-3pqrsc fontWeight: 620 -> 500",
+   "BUTTON.kopt press svelte-3pqrsc fontWeight: 500 -> 620",
+ ]

  82 |   const search = await flashes(page, '.palette')
  83 |   expect(search.seen).toBeGreaterThan(3)
> 84 |   expect(search.bad, 'search: unstyled on its first frame').toEqual([])
     |                                                             ^
  85 |   await page.keyboard.press('Escape')
  86 |
  87 |   // The shared menu also loads its styles before its first open.
    at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/lazy-styled.spec.ts:84:61

3. chromium-1440 · search-corpus.spec.ts:44

every prefix, the full word, one-letter and two-letter typos find their note

First error

Error: missed: prefix "ma" -> marmalade (rank 5); 1 typo "mrmalade" -> marmalade (rank -1); 1 typo "amrmalade" -> marmalade (rank -1); 1 typo "mermalade" -> marmalade (rank -1); 1 typo "mxarmalade" -> marmalade (rank -1); 1 typo "mamalade" -> marmalade (rank -1); 1 typo "mramalade" -> marmalade (rank -1); 1 typo "maemalade" -> marmalade (rank -1); 1 typo "maxrmalade" -> marmalade (rank -1); 1 typo "maralade" -> marmalade (rank -1); 1 typo "mamralade" -> marmalade (rank -1); 1 typo "marealade" -> marmalade (rank -1)

expect(received).toBeLessThanOrEqual(expected)

Expected: <= 0.02
Received:    0.7151474530831099

  110 |   console.log(`search corpus server time (took_ms), limit ${LIMIT}: ${served.length} queries, p50 ${q(0.5)} ms, p95 ${q(0.95)} ms, p99 ${q(0.99)} ms`)
  111 |   if (misses.length) console.log(misses.slice(0, 40).join('\n'))
> 112 |   expect(misses.length / asked, `missed: ${misses.slice(0, 12).join('; ')}`).toBeLessThanOrEqual(0.02)
      |                                                                              ^
  113 |   } finally { await clearCorpus() }
  114 |   // Latency is not asserted here: this goes through whatever sits between the test and the server (the Studio's
  115 |   // tunnel adds ~150 ms). The server's own numbers come from the perf gate (perf/budgets.json, search.query).
    at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/search-corpus.spec.ts:112:78

4. chromium-390 · touch-select.spec.ts:140

a scroll stays a scroll

First error

Error: expect(received).toBe(expected) // Object.is equality

Expected: true
Received: false

  145 |     { type: 'touchend', x: x + 12, y: y + 90 },
  146 |   ])
> 147 |   expect(prevented.every((p) => !p)).toBe(true)
      |                                      ^
  148 |   await expect(blockBar(page)).toHaveCount(0)
  149 | })
  150 |
    at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/touch-select.spec.ts:147:38

5. chromium-1440 · vim.spec.ts:341

/ searches in normal mode, in the bar; in insert mode it is a slash

First error

Error: expect(locator).toHaveCount(expected) failed

Locator:  locator('.cm-searchMatch')
Expected: 0
Received: 3
Timeout:  5000ms

Call log:
  - Expect "toHaveCount" locator('.cm-searchMatch') with timeout 5000ms
  - waiting for locator('.cm-searchMatch')
    14 × locator resolved to 3 elements
       - unexpected value "3"


  370 |     await typeKeys(page, 'noh')
  371 |     await keys(page, 'Enter')
> 372 |     await expect(page.locator('.cm-searchMatch')).toHaveCount(0)
      |                                                   ^
  373 |     // Insert mode: `/` is just a slash.
  374 |     await keys(page, 'A')
  375 |     await typeKeys(page, ' a/b')
    at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/vim.spec.ts:372:51

6. contrast · contrast.spec.ts:155

the palette keeps its contrast at every sky hour

First error

Error: light/dawn/search: 3.45 (needs 4.5) .rt "Kept-xcoxud"
light/dawn/search: 3.45 (needs 4.5) .svelte-1jp1e9h "Kept"
light/dawn/search: 3.69 (needs 4.5) .rt "Kept-mgq9su"
light/dawn/search: 3.69 (needs 4.5) .svelte-1jp1e9h "Kept"
light/dawn/search: 3.65 (needs 4.5) .rt "Kept-eveic1"
light/dawn/search: 3.65 (needs 4.5) .svelte-1jp1e9h "Kept"
light/dawn/search: 3.67 (needs 4.5) .rt "Kept-4v86y2"
light/dawn/search: 3.67 (needs 4.5) .svelte-1jp1e9h "Kept"
light/dawn/search: 1.41 (needs 4.5) .cm-line "Kept-xcoxud"
light/day/search: 3.53 (needs 4.5) .rt "Kept-xcoxud"
light/day/search: 3.53 (needs 4.5) .svelte-1jp1e9h "Kept"
light/day/search: 3.80 (needs 4.5) .rt "Kept-mgq9su"
light/day/search: 3.80 (needs 4.5) .svelte-1jp1e9h "Kept"
light/day/search: 3.70 (needs 4.5) .rt "Kept-eveic1"
light/day/search: 3.70 (needs 4.5) .svelte-1jp1e9h "Kept"
light/day/search: 3.70 (needs 4.5) .rt "Kept-4v86y2"
light/day/search: 3.70 (needs 4.5) .svelte-1jp1e9h "Kept"
light/day/search: 1.41 (needs 4.5) .cm-line "Kept-xcoxud"
light/dusk/search: 3.60 (needs 4.5) .rt "Kept-xcoxud"
light/dusk/search: 3.60 (needs 4.5) .svelte-1jp1e9h "Kept"
light/dusk/search: 3.91 (needs 4.5) .rt "Kept-mgq9su"
light/dusk/search: 3.91 (needs 4.5) .svelte-1jp1e9h "Kept"
light/dusk/search: 3.88 (needs 4.5) .rt "Kept-eveic1"
light/dusk/search: 3.88 (needs 4.5) .svelte-1jp1e9h "Kept"
light/dusk/search: 3.89 (needs 4.5) .rt "Kept-4v86y2"
light/dusk/search: 3.89 (needs 4.5) .svelte-1jp1e9h "Kept"
light/dusk/search: 1.41 (needs 4.5) .cm-line "Kept-xcoxud"
light/night/search: 3.49 (needs 4.5) .rt "Kept-xcoxud"
light/night/search: 3.49 (needs 4.5) .svelte-1jp1e9h "Kept"
light/night/search: 3.80 (needs 4.5) .rt "Kept-mgq9su"
light/night/search: 3.80 (needs 4.5) .svelte-1jp1e9h "Kept"
light/night/search: 3.76 (needs 4.5) .rt "Kept-eveic1"
light/night/search: 3.76 (needs 4.5) .svelte-1jp1e9h "Kept"
light/night/search: 3.76 (needs 4.5) .rt "Kept-4v86y2"
light/night/search: 3.76 (needs 4.5) .svelte-1jp1e9h "Kept"
light/night/search: 1.41 (needs 4.5) .cm-line "Kept-xcoxud"
dark/dawn/search: 3.40 (needs 4.5) .rt "Kept-xcoxud"
dark/dawn/search: 3.40 (needs 4.5) .svelte-1jp1e9h "Kept"
dark/dawn/search: 3.83 (needs 4.5) .rt "Kept-mgq9su"
dark/dawn/search: 3.83 (needs 4.5) .svelte-1jp1e9h "Kept"
dark/dawn/search: 3.88 (needs 4.5) .rt "Kept-eveic1"
dark/dawn/search: 3.88 (needs 4.5) .svelte-1jp1e9h "Kept"
dark/dawn/search: 3.83 (needs 4.5) .rt "Kept-4v86y2"
dark/dawn/search: 3.83 (needs 4.5) .svelte-1jp1e9h "Kept"
dark/dawn/search: 1.74 (needs 4.5) .cm-line "Kept-xcoxud"
dark/day/search: 3.38 (needs 4.5) .rt "Kept-xcoxud"
dark/day/search: 3.38 (needs 4.5) .svelte-1jp1e9h "Kept"
dark/day/search: 3.76 (needs 4.5) .rt "Kept-mgq9su"
dark/day/search: 3.76 (needs 4.5) .svelte-1jp1e9h "Kept"
dark/day/search: 3.86 (needs 4.5) .rt "Kept-eveic1"
dark/day/search: 3.86 (needs 4.5) .svelte-1jp1e9h "Kept"
dark/day/search: 3.88 (needs 4.5) .rt "Kept-4v86y2"
dark/day/search: 3.88 (needs 4.5) .svelte-1jp1e9h "Kept"
dark/day/search: 1.74 (needs 4.5) .cm-line "Kept-xcoxud"
dark/dusk/search: 3.43 (needs 4.5) .rt "Kept-xcoxud"
dark/dusk/search: 3.43 (needs 4.5) .svelte-1jp1e9h "Kept"
dark/dusk/search: 3.86 (needs 4.5) .rt "Kept-mgq9su"
dark/dusk/search: 3.86 (needs 4.5) .svelte-1jp1e9h "Kept"
dark/dusk/search: 3.93 (needs 4.5) .rt "Kept-eveic1"
dark/dusk/search: 3.93 (needs 4.5) .svelte-1jp1e9h "Kept"
dark/dusk/search: 3.93 (needs 4.5) .rt "Kept-4v86y2"
dark/dusk/search: 3.93 (needs 4.5) .svelte-1jp1e9h "Kept"
dark/dusk/search: 1.74 (needs 4.5) .cm-line "Kept-xcoxud"
dark/night/search: 3.54 (needs 4.5) .rt "Kept-xcoxud"
dark/night/search: 3.54 (needs 4.5) .svelte-1jp1e9h "Kept"
dark/night/search: 4.02 (needs 4.5) .rt "Kept-mgq9su"
dark/night/search: 4.02 (needs 4.5) .svelte-1jp1e9h "Kept"
dark/night/search: 4.02 (needs 4.5) .rt "Kept-eveic1"
dark/night/search: 4.02 (needs 4.5) .svelte-1jp1e9h "Kept"
dark/night/search: 4.02 (needs 4.5) .rt "Kept-4v86y2"
dark/night/search: 4.02 (needs 4.5) .svelte-1jp1e9h "Kept"
dark/night/search: 1.74 (needs 4.5) .cm-line "Kept-xcoxud"

expect(received).toEqual(expected) // deep equality

- Expected  -  1
+ Received  + 74

- Array []
+ Array [
+   "light/dawn/search: 3.45 (needs 4.5) .rt \"Kept-xcoxud\"",
+   "light/dawn/search: 3.45 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "light/dawn/search: 3.69 (needs 4.5) .rt \"Kept-mgq9su\"",
+   "light/dawn/search: 3.69 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "light/dawn/search: 3.65 (needs 4.5) .rt \"Kept-eveic1\"",
+   "light/dawn/search: 3.65 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "light/dawn/search: 3.67 (needs 4.5) .rt \"Kept-4v86y2\"",
+   "light/dawn/search: 3.67 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "light/dawn/search: 1.41 (needs 4.5) .cm-line \"Kept-xcoxud\"",
+   "light/day/search: 3.53 (needs 4.5) .rt \"Kept-xcoxud\"",
+   "light/day/search: 3.53 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "light/day/search: 3.80 (needs 4.5) .rt \"Kept-mgq9su\"",
+   "light/day/search: 3.80 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "light/day/search: 3.70 (needs 4.5) .rt \"Kept-eveic1\"",
+   "light/day/search: 3.70 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "light/day/search: 3.70 (needs 4.5) .rt \"Kept-4v86y2\"",
+   "light/day/search: 3.70 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "light/day/search: 1.41 (needs 4.5) .cm-line \"Kept-xcoxud\"",
+   "light/dusk/search: 3.60 (needs 4.5) .rt \"Kept-xcoxud\"",
+   "light/dusk/search: 3.60 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "light/dusk/search: 3.91 (needs 4.5) .rt \"Kept-mgq9su\"",
+   "light/dusk/search: 3.91 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "light/dusk/search: 3.88 (needs 4.5) .rt \"Kept-eveic1\"",
+   "light/dusk/search: 3.88 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "light/dusk/search: 3.89 (needs 4.5) .rt \"Kept-4v86y2\"",
+   "light/dusk/search: 3.89 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "light/dusk/search: 1.41 (needs 4.5) .cm-line \"Kept-xcoxud\"",
+   "light/night/search: 3.49 (needs 4.5) .rt \"Kept-xcoxud\"",
+   "light/night/search: 3.49 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "light/night/search: 3.80 (needs 4.5) .rt \"Kept-mgq9su\"",
+   "light/night/search: 3.80 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "light/night/search: 3.76 (needs 4.5) .rt \"Kept-eveic1\"",
+   "light/night/search: 3.76 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "light/night/search: 3.76 (needs 4.5) .rt \"Kept-4v86y2\"",
+   "light/night/search: 3.76 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "light/night/search: 1.41 (needs 4.5) .cm-line \"Kept-xcoxud\"",
+   "dark/dawn/search: 3.40 (needs 4.5) .rt \"Kept-xcoxud\"",
+   "dark/dawn/search: 3.40 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "dark/dawn/search: 3.83 (needs 4.5) .rt \"Kept-mgq9su\"",
+   "dark/dawn/search: 3.83 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "dark/dawn/search: 3.88 (needs 4.5) .rt \"Kept-eveic1\"",
+   "dark/dawn/search: 3.88 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "dark/dawn/search: 3.83 (needs 4.5) .rt \"Kept-4v86y2\"",
+   "dark/dawn/search: 3.83 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "dark/dawn/search: 1.74 (needs 4.5) .cm-line \"Kept-xcoxud\"",
+   "dark/day/search: 3.38 (needs 4.5) .rt \"Kept-xcoxud\"",
+   "dark/day/search: 3.38 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "dark/day/search: 3.76 (needs 4.5) .rt \"Kept-mgq9su\"",
+   "dark/day/search: 3.76 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "dark/day/search: 3.86 (needs 4.5) .rt \"Kept-eveic1\"",
+   "dark/day/search: 3.86 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "dark/day/search: 3.88 (needs 4.5) .rt \"Kept-4v86y2\"",
+   "dark/day/search: 3.88 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "dark/day/search: 1.74 (needs 4.5) .cm-line \"Kept-xcoxud\"",
+   "dark/dusk/search: 3.43 (needs 4.5) .rt \"Kept-xcoxud\"",
+   "dark/dusk/search: 3.43 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "dark/dusk/search: 3.86 (needs 4.5) .rt \"Kept-mgq9su\"",
+   "dark/dusk/search: 3.86 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "dark/dusk/search: 3.93 (needs 4.5) .rt \"Kept-eveic1\"",
+   "dark/dusk/search: 3.93 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "dark/dusk/search: 3.93 (needs 4.5) .rt \"Kept-4v86y2\"",
+   "dark/dusk/search: 3.93 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "dark/dusk/search: 1.74 (needs 4.5) .cm-line \"Kept-xcoxud\"",
+   "dark/night/search: 3.54 (needs 4.5) .rt \"Kept-xcoxud\"",
+   "dark/night/search: 3.54 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "dark/night/search: 4.02 (needs 4.5) .rt \"Kept-mgq9su\"",
+   "dark/night/search: 4.02 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "dark/night/search: 4.02 (needs 4.5) .rt \"Kept-eveic1\"",
+   "dark/night/search: 4.02 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "dark/night/search: 4.02 (needs 4.5) .rt \"Kept-4v86y2\"",
+   "dark/night/search: 4.02 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "dark/night/search: 1.74 (needs 4.5) .cm-line \"Kept-xcoxud\"",
+ ]

  53 | const finish = (report: string[], failures: string[], file?: string) => {
  54 |   if (file) writeFileSync(file, report.join('\n') + '\n')
> 55 |   expect(failures, failures.join('\n')).toEqual([])
     |                                         ^
  56 | }
  57 |
  58 | test('text and icons on the glass keep their contrast at every sky hour', async ({ page }) => {
    at finish (/home/claude/Developer/Worktrees/bm8/apps/web/e2e/contrast.spec.ts:55:41)
    at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/contrast.spec.ts:167:3

7. contrast · contrast.spec.ts:171

the palette keeps its contrast over a photo and over a page of huge words, 1440 wide

First error

Error: 1440/light/photo/search: 3.64 (needs 4.5) .rt "Kept-xcoxud"
1440/light/photo/search: 3.64 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/light/photo/search: 3.87 (needs 4.5) .rt "Kept-dh9oq2"
1440/light/photo/search: 3.87 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/light/photo/search: 3.82 (needs 4.5) .rt "Kept-eveic1"
1440/light/photo/search: 3.82 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/light/photo/search: 3.75 (needs 4.5) .rt "Kept-mgq9su"
1440/light/photo/search: 3.75 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/light/photo/search: 3.68 (needs 4.5) .rt "Kept-4v86y2"
1440/light/photo/search: 3.68 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/light/photo/search: 1.41 (needs 4.5) .cm-line "Kept-xcoxud"
1440/light/photo + huge words/search: 3.35 (needs 4.5) .rt "Kept-xcoxud"
1440/light/photo + huge words/search: 3.35 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/light/photo + huge words/search: 3.51 (needs 4.5) .rt "Kept-dh9oq2"
1440/light/photo + huge words/search: 3.51 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/light/photo + huge words/search: 3.46 (needs 4.5) .rt "Kept-eveic1"
1440/light/photo + huge words/search: 3.46 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/light/photo + huge words/search: 3.39 (needs 4.5) .rt "Kept-mgq9su"
1440/light/photo + huge words/search: 3.39 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/light/photo + huge words/search: 3.34 (needs 4.5) .rt "Kept-4v86y2"
1440/light/photo + huge words/search: 3.34 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/light/photo + huge words/search: 1.73 (needs 4.5) .cm-line "Kept-xcoxud"
1440/light/photo + huge words/search: 1.73 (needs 4.5) .hit "Kept"
1440/dark/photo/search: 3.42 (needs 4.5) .rt "Kept-xcoxud"
1440/dark/photo/search: 3.42 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/dark/photo/search: 3.85 (needs 4.5) .rt "Kept-dh9oq2"
1440/dark/photo/search: 3.85 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/dark/photo/search: 3.85 (needs 4.5) .rt "Kept-eveic1"
1440/dark/photo/search: 3.85 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/dark/photo/search: 3.85 (needs 4.5) .rt "Kept-mgq9su"
1440/dark/photo/search: 3.85 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/dark/photo/search: 3.91 (needs 4.5) .rt "Kept-4v86y2"
1440/dark/photo/search: 3.91 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/dark/photo/search: 1.74 (needs 4.5) .cm-line "Kept-xcoxud"
1440/dark/photo + huge words/search: 3.32 (needs 4.5) .rt "Kept-xcoxud"
1440/dark/photo + huge words/search: 3.32 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/dark/photo + huge words/search: 3.65 (needs 4.5) .rt "Kept-dh9oq2"
1440/dark/photo + huge words/search: 3.65 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/dark/photo + huge words/search: 3.65 (needs 4.5) .rt "Kept-eveic1"
1440/dark/photo + huge words/search: 3.65 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/dark/photo + huge words/search: 3.69 (needs 4.5) .rt "Kept-mgq9su"
1440/dark/photo + huge words/search: 3.69 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/dark/photo + huge words/search: 3.69 (needs 4.5) .rt "Kept-4v86y2"
1440/dark/photo + huge words/search: 3.69 (needs 4.5) .svelte-1jp1e9h "Kept"
1440/dark/photo + huge words/search: 1.99 (needs 4.5) .cm-line "Kept-xcoxud"
1440/dark/photo + huge words/search: 1.99 (needs 4.5) .hit "Kept"

expect(received).toEqual(expected) // deep equality

- Expected  -  1
+ Received  + 48

- Array []
+ Array [
+   "1440/light/photo/search: 3.64 (needs 4.5) .rt \"Kept-xcoxud\"",
+   "1440/light/photo/search: 3.64 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/light/photo/search: 3.87 (needs 4.5) .rt \"Kept-dh9oq2\"",
+   "1440/light/photo/search: 3.87 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/light/photo/search: 3.82 (needs 4.5) .rt \"Kept-eveic1\"",
+   "1440/light/photo/search: 3.82 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/light/photo/search: 3.75 (needs 4.5) .rt \"Kept-mgq9su\"",
+   "1440/light/photo/search: 3.75 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/light/photo/search: 3.68 (needs 4.5) .rt \"Kept-4v86y2\"",
+   "1440/light/photo/search: 3.68 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/light/photo/search: 1.41 (needs 4.5) .cm-line \"Kept-xcoxud\"",
+   "1440/light/photo + huge words/search: 3.35 (needs 4.5) .rt \"Kept-xcoxud\"",
+   "1440/light/photo + huge words/search: 3.35 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/light/photo + huge words/search: 3.51 (needs 4.5) .rt \"Kept-dh9oq2\"",
+   "1440/light/photo + huge words/search: 3.51 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/light/photo + huge words/search: 3.46 (needs 4.5) .rt \"Kept-eveic1\"",
+   "1440/light/photo + huge words/search: 3.46 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/light/photo + huge words/search: 3.39 (needs 4.5) .rt \"Kept-mgq9su\"",
+   "1440/light/photo + huge words/search: 3.39 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/light/photo + huge words/search: 3.34 (needs 4.5) .rt \"Kept-4v86y2\"",
+   "1440/light/photo + huge words/search: 3.34 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/light/photo + huge words/search: 1.73 (needs 4.5) .cm-line \"Kept-xcoxud\"",
+   "1440/light/photo + huge words/search: 1.73 (needs 4.5) .hit \"Kept\"",
+   "1440/dark/photo/search: 3.42 (needs 4.5) .rt \"Kept-xcoxud\"",
+   "1440/dark/photo/search: 3.42 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/dark/photo/search: 3.85 (needs 4.5) .rt \"Kept-dh9oq2\"",
+   "1440/dark/photo/search: 3.85 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/dark/photo/search: 3.85 (needs 4.5) .rt \"Kept-eveic1\"",
+   "1440/dark/photo/search: 3.85 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/dark/photo/search: 3.85 (needs 4.5) .rt \"Kept-mgq9su\"",
+   "1440/dark/photo/search: 3.85 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/dark/photo/search: 3.91 (needs 4.5) .rt \"Kept-4v86y2\"",
+   "1440/dark/photo/search: 3.91 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/dark/photo/search: 1.74 (needs 4.5) .cm-line \"Kept-xcoxud\"",
+   "1440/dark/photo + huge words/search: 3.32 (needs 4.5) .rt \"Kept-xcoxud\"",
+   "1440/dark/photo + huge words/search: 3.32 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/dark/photo + huge words/search: 3.65 (needs 4.5) .rt \"Kept-dh9oq2\"",
+   "1440/dark/photo + huge words/search: 3.65 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/dark/photo + huge words/search: 3.65 (needs 4.5) .rt \"Kept-eveic1\"",
+   "1440/dark/photo + huge words/search: 3.65 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/dark/photo + huge words/search: 3.69 (needs 4.5) .rt \"Kept-mgq9su\"",
+   "1440/dark/photo + huge words/search: 3.69 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/dark/photo + huge words/search: 3.69 (needs 4.5) .rt \"Kept-4v86y2\"",
+   "1440/dark/photo + huge words/search: 3.69 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "1440/dark/photo + huge words/search: 1.99 (needs 4.5) .cm-line \"Kept-xcoxud\"",
+   "1440/dark/photo + huge words/search: 1.99 (needs 4.5) .hit \"Kept\"",
+ ]

  53 | const finish = (report: string[], failures: string[], file?: string) => {
  54 |   if (file) writeFileSync(file, report.join('\n') + '\n')
> 55 |   expect(failures, failures.join('\n')).toEqual([])
     |                                         ^
  56 | }
  57 |
  58 | test('text and icons on the glass keep their contrast at every sky hour', async ({ page }) => {
    at finish (/home/claude/Developer/Worktrees/bm8/apps/web/e2e/contrast.spec.ts:55:41)
    at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/contrast.spec.ts:186:5

8. contrast · contrast.spec.ts:171

the palette keeps its contrast over a photo and over a page of huge words, 390 wide

First error

Error: 390/light/photo/search: 3.75 (needs 4.5) .rt "Kept-xcoxud"
390/light/photo/search: 3.75 (needs 4.5) .svelte-1jp1e9h "Kept"
390/light/photo/search: 4.03 (needs 4.5) .rt "Kept-eveic1"
390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept"
390/light/photo/search: 4.03 (needs 4.5) .rt "Kept-linv2c"
390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept"
390/light/photo/search: 4.03 (needs 4.5) .rt "Kept-4v86y2"
390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept"
390/light/photo/search: 4.03 (needs 4.5) .rt "Kept-dh9oq2"
390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept"
390/light/photo/search: 4.03 (needs 4.5) .rt "Kept-mgq9su"
390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept"
390/light/photo + huge words/search: 3.75 (needs 4.5) .rt "Kept-xcoxud"
390/light/photo + huge words/search: 3.75 (needs 4.5) .svelte-1jp1e9h "Kept"
390/light/photo + huge words/search: 4.03 (needs 4.5) .rt "Kept-eveic1"
390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept"
390/light/photo + huge words/search: 4.03 (needs 4.5) .rt "Kept-linv2c"
390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept"
390/light/photo + huge words/search: 4.03 (needs 4.5) .rt "Kept-4v86y2"
390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept"
390/light/photo + huge words/search: 4.03 (needs 4.5) .rt "Kept-dh9oq2"
390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept"
390/light/photo + huge words/search: 4.03 (needs 4.5) .rt "Kept-mgq9su"
390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept"
390/dark/photo/search: 3.67 (needs 4.5) .rt "Kept-xcoxud"
390/dark/photo/search: 3.67 (needs 4.5) .svelte-1jp1e9h "Kept"
390/dark/photo/search: 4.08 (needs 4.5) .rt "Kept-eveic1"
390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept"
390/dark/photo/search: 4.08 (needs 4.5) .rt "Kept-linv2c"
390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept"
390/dark/photo/search: 4.08 (needs 4.5) .rt "Kept-4v86y2"
390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept"
390/dark/photo/search: 4.08 (needs 4.5) .rt "Kept-dh9oq2"
390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept"
390/dark/photo/search: 4.08 (needs 4.5) .rt "Kept-mgq9su"
390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept"
390/dark/photo + huge words/search: 3.67 (needs 4.5) .rt "Kept-xcoxud"
390/dark/photo + huge words/search: 3.67 (needs 4.5) .svelte-1jp1e9h "Kept"
390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt "Kept-eveic1"
390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept"
390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt "Kept-linv2c"
390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept"
390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt "Kept-4v86y2"
390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept"
390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt "Kept-dh9oq2"
390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept"
390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt "Kept-mgq9su"
390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept"

expect(received).toEqual(expected) // deep equality

- Expected  -  1
+ Received  + 50

- Array []
+ Array [
+   "390/light/photo/search: 3.75 (needs 4.5) .rt \"Kept-xcoxud\"",
+   "390/light/photo/search: 3.75 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/light/photo/search: 4.03 (needs 4.5) .rt \"Kept-eveic1\"",
+   "390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/light/photo/search: 4.03 (needs 4.5) .rt \"Kept-linv2c\"",
+   "390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/light/photo/search: 4.03 (needs 4.5) .rt \"Kept-4v86y2\"",
+   "390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/light/photo/search: 4.03 (needs 4.5) .rt \"Kept-dh9oq2\"",
+   "390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/light/photo/search: 4.03 (needs 4.5) .rt \"Kept-mgq9su\"",
+   "390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/light/photo + huge words/search: 3.75 (needs 4.5) .rt \"Kept-xcoxud\"",
+   "390/light/photo + huge words/search: 3.75 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/light/photo + huge words/search: 4.03 (needs 4.5) .rt \"Kept-eveic1\"",
+   "390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/light/photo + huge words/search: 4.03 (needs 4.5) .rt \"Kept-linv2c\"",
+   "390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/light/photo + huge words/search: 4.03 (needs 4.5) .rt \"Kept-4v86y2\"",
+   "390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/light/photo + huge words/search: 4.03 (needs 4.5) .rt \"Kept-dh9oq2\"",
+   "390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/light/photo + huge words/search: 4.03 (needs 4.5) .rt \"Kept-mgq9su\"",
+   "390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/dark/photo/search: 3.67 (needs 4.5) .rt \"Kept-xcoxud\"",
+   "390/dark/photo/search: 3.67 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/dark/photo/search: 4.08 (needs 4.5) .rt \"Kept-eveic1\"",
+   "390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/dark/photo/search: 4.08 (needs 4.5) .rt \"Kept-linv2c\"",
+   "390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/dark/photo/search: 4.08 (needs 4.5) .rt \"Kept-4v86y2\"",
+   "390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/dark/photo/search: 4.08 (needs 4.5) .rt \"Kept-dh9oq2\"",
+   "390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/dark/photo/search: 4.08 (needs 4.5) .rt \"Kept-mgq9su\"",
+   "390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/dark/photo + huge words/search: 3.67 (needs 4.5) .rt \"Kept-xcoxud\"",
+   "390/dark/photo + huge words/search: 3.67 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt \"Kept-eveic1\"",
+   "390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt \"Kept-linv2c\"",
+   "390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt \"Kept-4v86y2\"",
+   "390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt \"Kept-dh9oq2\"",
+   "390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+   "390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt \"Kept-mgq9su\"",
+   "390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"",
+ ]

  53 | const finish = (report: string[], failures: string[], file?: string) => {
  54 |   if (file) writeFileSync(file, report.join('\n') + '\n')
> 55 |   expect(failures, failures.join('\n')).toEqual([])
     |                                         ^
  56 | }
  57 |
  58 | test('text and icons on the glass keep their contrast at every sky hour', async ({ page }) => {
    at finish (/home/claude/Developer/Worktrees/bm8/apps/web/e2e/contrast.spec.ts:55:41)
    at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/contrast.spec.ts:186:5
Chromium/dev-01 **complete first-error catalog, all 8 failures**. Head `4d02e704a281dda00655f45aca6d0d6744fb6357`. Every block is the first attempt, no retries. ### 1. chromium-390 · glass.spec.ts:341 every floating surface is the one glass First error ```text Error: search: the bar's shade, edge and shadow expect(received).toBe(expected) // Object.is equality Expected: "rgba(16, 15, 14, 0.3) 0px 0px 20px 0px inset, rgba(255, 255, 255, 0.1) 0px 0px 0px 1px inset, rgba(255, 255, 255, 0.14) 0px 1px 0px 0px inset, rgba(255, 255, 255, 0.08) 0px 0px 0px 1px, rgba(0, 0, 0, 0.55) 0px 12px 30px -10px" Received: "rgba(16, 15, 14, 0.3) 0px 0px 20px 0px inset, rgba(255, 255, 255, 0.1) 0px 0px 0px 1px inset, rgba(255, 255, 255, 0.14) 0px 1px 0px 0px inset" 369 | const same = (m: typeof bar, dense: boolean, what: string) => { 370 | expect(m.filter, `${what}: the bar's blur`).toBe(bar.filter) > 371 | expect(m.shadow, `${what}: the bar's shade, edge and shadow`).toBe(bar.shadow) | ^ 372 | expect(m.kids, `${what}: its glass holds nothing`).toBe(0) 373 | return tint(dense).then((t) => expect(m.bg, `${what}: its tint`).toBe(t)) 374 | } at same (/home/claude/Developer/Worktrees/bm8/apps/web/e2e/glass.spec.ts:371:67) at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/glass.spec.ts:379:9 ``` ### 2. chromium-390 · lazy-styled.spec.ts:54 lazily loaded sheets, search and menu are styled on their first frame First error ```text Error: search: unstyled on its first frame expect(received).toEqual(expected) // deep equality - Expected - 1 + Received + 4 - Array [] + Array [ + "BUTTON.kopt press svelte-3pqrsc fontWeight: 620 -> 500", + "BUTTON.kopt press svelte-3pqrsc fontWeight: 500 -> 620", + ] 82 | const search = await flashes(page, '.palette') 83 | expect(search.seen).toBeGreaterThan(3) > 84 | expect(search.bad, 'search: unstyled on its first frame').toEqual([]) | ^ 85 | await page.keyboard.press('Escape') 86 | 87 | // The shared menu also loads its styles before its first open. at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/lazy-styled.spec.ts:84:61 ``` ### 3. chromium-1440 · search-corpus.spec.ts:44 every prefix, the full word, one-letter and two-letter typos find their note First error ```text Error: missed: prefix "ma" -> marmalade (rank 5); 1 typo "mrmalade" -> marmalade (rank -1); 1 typo "amrmalade" -> marmalade (rank -1); 1 typo "mermalade" -> marmalade (rank -1); 1 typo "mxarmalade" -> marmalade (rank -1); 1 typo "mamalade" -> marmalade (rank -1); 1 typo "mramalade" -> marmalade (rank -1); 1 typo "maemalade" -> marmalade (rank -1); 1 typo "maxrmalade" -> marmalade (rank -1); 1 typo "maralade" -> marmalade (rank -1); 1 typo "mamralade" -> marmalade (rank -1); 1 typo "marealade" -> marmalade (rank -1) expect(received).toBeLessThanOrEqual(expected) Expected: <= 0.02 Received: 0.7151474530831099 110 | console.log(`search corpus server time (took_ms), limit ${LIMIT}: ${served.length} queries, p50 ${q(0.5)} ms, p95 ${q(0.95)} ms, p99 ${q(0.99)} ms`) 111 | if (misses.length) console.log(misses.slice(0, 40).join('\n')) > 112 | expect(misses.length / asked, `missed: ${misses.slice(0, 12).join('; ')}`).toBeLessThanOrEqual(0.02) | ^ 113 | } finally { await clearCorpus() } 114 | // Latency is not asserted here: this goes through whatever sits between the test and the server (the Studio's 115 | // tunnel adds ~150 ms). The server's own numbers come from the perf gate (perf/budgets.json, search.query). at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/search-corpus.spec.ts:112:78 ``` ### 4. chromium-390 · touch-select.spec.ts:140 a scroll stays a scroll First error ```text Error: expect(received).toBe(expected) // Object.is equality Expected: true Received: false 145 | { type: 'touchend', x: x + 12, y: y + 90 }, 146 | ]) > 147 | expect(prevented.every((p) => !p)).toBe(true) | ^ 148 | await expect(blockBar(page)).toHaveCount(0) 149 | }) 150 | at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/touch-select.spec.ts:147:38 ``` ### 5. chromium-1440 · vim.spec.ts:341 / searches in normal mode, in the bar; in insert mode it is a slash First error ```text Error: expect(locator).toHaveCount(expected) failed Locator: locator('.cm-searchMatch') Expected: 0 Received: 3 Timeout: 5000ms Call log: - Expect "toHaveCount" locator('.cm-searchMatch') with timeout 5000ms - waiting for locator('.cm-searchMatch') 14 × locator resolved to 3 elements - unexpected value "3" 370 | await typeKeys(page, 'noh') 371 | await keys(page, 'Enter') > 372 | await expect(page.locator('.cm-searchMatch')).toHaveCount(0) | ^ 373 | // Insert mode: `/` is just a slash. 374 | await keys(page, 'A') 375 | await typeKeys(page, ' a/b') at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/vim.spec.ts:372:51 ``` ### 6. contrast · contrast.spec.ts:155 the palette keeps its contrast at every sky hour First error ```text Error: light/dawn/search: 3.45 (needs 4.5) .rt "Kept-xcoxud" light/dawn/search: 3.45 (needs 4.5) .svelte-1jp1e9h "Kept" light/dawn/search: 3.69 (needs 4.5) .rt "Kept-mgq9su" light/dawn/search: 3.69 (needs 4.5) .svelte-1jp1e9h "Kept" light/dawn/search: 3.65 (needs 4.5) .rt "Kept-eveic1" light/dawn/search: 3.65 (needs 4.5) .svelte-1jp1e9h "Kept" light/dawn/search: 3.67 (needs 4.5) .rt "Kept-4v86y2" light/dawn/search: 3.67 (needs 4.5) .svelte-1jp1e9h "Kept" light/dawn/search: 1.41 (needs 4.5) .cm-line "Kept-xcoxud" light/day/search: 3.53 (needs 4.5) .rt "Kept-xcoxud" light/day/search: 3.53 (needs 4.5) .svelte-1jp1e9h "Kept" light/day/search: 3.80 (needs 4.5) .rt "Kept-mgq9su" light/day/search: 3.80 (needs 4.5) .svelte-1jp1e9h "Kept" light/day/search: 3.70 (needs 4.5) .rt "Kept-eveic1" light/day/search: 3.70 (needs 4.5) .svelte-1jp1e9h "Kept" light/day/search: 3.70 (needs 4.5) .rt "Kept-4v86y2" light/day/search: 3.70 (needs 4.5) .svelte-1jp1e9h "Kept" light/day/search: 1.41 (needs 4.5) .cm-line "Kept-xcoxud" light/dusk/search: 3.60 (needs 4.5) .rt "Kept-xcoxud" light/dusk/search: 3.60 (needs 4.5) .svelte-1jp1e9h "Kept" light/dusk/search: 3.91 (needs 4.5) .rt "Kept-mgq9su" light/dusk/search: 3.91 (needs 4.5) .svelte-1jp1e9h "Kept" light/dusk/search: 3.88 (needs 4.5) .rt "Kept-eveic1" light/dusk/search: 3.88 (needs 4.5) .svelte-1jp1e9h "Kept" light/dusk/search: 3.89 (needs 4.5) .rt "Kept-4v86y2" light/dusk/search: 3.89 (needs 4.5) .svelte-1jp1e9h "Kept" light/dusk/search: 1.41 (needs 4.5) .cm-line "Kept-xcoxud" light/night/search: 3.49 (needs 4.5) .rt "Kept-xcoxud" light/night/search: 3.49 (needs 4.5) .svelte-1jp1e9h "Kept" light/night/search: 3.80 (needs 4.5) .rt "Kept-mgq9su" light/night/search: 3.80 (needs 4.5) .svelte-1jp1e9h "Kept" light/night/search: 3.76 (needs 4.5) .rt "Kept-eveic1" light/night/search: 3.76 (needs 4.5) .svelte-1jp1e9h "Kept" light/night/search: 3.76 (needs 4.5) .rt "Kept-4v86y2" light/night/search: 3.76 (needs 4.5) .svelte-1jp1e9h "Kept" light/night/search: 1.41 (needs 4.5) .cm-line "Kept-xcoxud" dark/dawn/search: 3.40 (needs 4.5) .rt "Kept-xcoxud" dark/dawn/search: 3.40 (needs 4.5) .svelte-1jp1e9h "Kept" dark/dawn/search: 3.83 (needs 4.5) .rt "Kept-mgq9su" dark/dawn/search: 3.83 (needs 4.5) .svelte-1jp1e9h "Kept" dark/dawn/search: 3.88 (needs 4.5) .rt "Kept-eveic1" dark/dawn/search: 3.88 (needs 4.5) .svelte-1jp1e9h "Kept" dark/dawn/search: 3.83 (needs 4.5) .rt "Kept-4v86y2" dark/dawn/search: 3.83 (needs 4.5) .svelte-1jp1e9h "Kept" dark/dawn/search: 1.74 (needs 4.5) .cm-line "Kept-xcoxud" dark/day/search: 3.38 (needs 4.5) .rt "Kept-xcoxud" dark/day/search: 3.38 (needs 4.5) .svelte-1jp1e9h "Kept" dark/day/search: 3.76 (needs 4.5) .rt "Kept-mgq9su" dark/day/search: 3.76 (needs 4.5) .svelte-1jp1e9h "Kept" dark/day/search: 3.86 (needs 4.5) .rt "Kept-eveic1" dark/day/search: 3.86 (needs 4.5) .svelte-1jp1e9h "Kept" dark/day/search: 3.88 (needs 4.5) .rt "Kept-4v86y2" dark/day/search: 3.88 (needs 4.5) .svelte-1jp1e9h "Kept" dark/day/search: 1.74 (needs 4.5) .cm-line "Kept-xcoxud" dark/dusk/search: 3.43 (needs 4.5) .rt "Kept-xcoxud" dark/dusk/search: 3.43 (needs 4.5) .svelte-1jp1e9h "Kept" dark/dusk/search: 3.86 (needs 4.5) .rt "Kept-mgq9su" dark/dusk/search: 3.86 (needs 4.5) .svelte-1jp1e9h "Kept" dark/dusk/search: 3.93 (needs 4.5) .rt "Kept-eveic1" dark/dusk/search: 3.93 (needs 4.5) .svelte-1jp1e9h "Kept" dark/dusk/search: 3.93 (needs 4.5) .rt "Kept-4v86y2" dark/dusk/search: 3.93 (needs 4.5) .svelte-1jp1e9h "Kept" dark/dusk/search: 1.74 (needs 4.5) .cm-line "Kept-xcoxud" dark/night/search: 3.54 (needs 4.5) .rt "Kept-xcoxud" dark/night/search: 3.54 (needs 4.5) .svelte-1jp1e9h "Kept" dark/night/search: 4.02 (needs 4.5) .rt "Kept-mgq9su" dark/night/search: 4.02 (needs 4.5) .svelte-1jp1e9h "Kept" dark/night/search: 4.02 (needs 4.5) .rt "Kept-eveic1" dark/night/search: 4.02 (needs 4.5) .svelte-1jp1e9h "Kept" dark/night/search: 4.02 (needs 4.5) .rt "Kept-4v86y2" dark/night/search: 4.02 (needs 4.5) .svelte-1jp1e9h "Kept" dark/night/search: 1.74 (needs 4.5) .cm-line "Kept-xcoxud" expect(received).toEqual(expected) // deep equality - Expected - 1 + Received + 74 - Array [] + Array [ + "light/dawn/search: 3.45 (needs 4.5) .rt \"Kept-xcoxud\"", + "light/dawn/search: 3.45 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "light/dawn/search: 3.69 (needs 4.5) .rt \"Kept-mgq9su\"", + "light/dawn/search: 3.69 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "light/dawn/search: 3.65 (needs 4.5) .rt \"Kept-eveic1\"", + "light/dawn/search: 3.65 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "light/dawn/search: 3.67 (needs 4.5) .rt \"Kept-4v86y2\"", + "light/dawn/search: 3.67 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "light/dawn/search: 1.41 (needs 4.5) .cm-line \"Kept-xcoxud\"", + "light/day/search: 3.53 (needs 4.5) .rt \"Kept-xcoxud\"", + "light/day/search: 3.53 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "light/day/search: 3.80 (needs 4.5) .rt \"Kept-mgq9su\"", + "light/day/search: 3.80 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "light/day/search: 3.70 (needs 4.5) .rt \"Kept-eveic1\"", + "light/day/search: 3.70 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "light/day/search: 3.70 (needs 4.5) .rt \"Kept-4v86y2\"", + "light/day/search: 3.70 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "light/day/search: 1.41 (needs 4.5) .cm-line \"Kept-xcoxud\"", + "light/dusk/search: 3.60 (needs 4.5) .rt \"Kept-xcoxud\"", + "light/dusk/search: 3.60 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "light/dusk/search: 3.91 (needs 4.5) .rt \"Kept-mgq9su\"", + "light/dusk/search: 3.91 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "light/dusk/search: 3.88 (needs 4.5) .rt \"Kept-eveic1\"", + "light/dusk/search: 3.88 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "light/dusk/search: 3.89 (needs 4.5) .rt \"Kept-4v86y2\"", + "light/dusk/search: 3.89 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "light/dusk/search: 1.41 (needs 4.5) .cm-line \"Kept-xcoxud\"", + "light/night/search: 3.49 (needs 4.5) .rt \"Kept-xcoxud\"", + "light/night/search: 3.49 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "light/night/search: 3.80 (needs 4.5) .rt \"Kept-mgq9su\"", + "light/night/search: 3.80 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "light/night/search: 3.76 (needs 4.5) .rt \"Kept-eveic1\"", + "light/night/search: 3.76 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "light/night/search: 3.76 (needs 4.5) .rt \"Kept-4v86y2\"", + "light/night/search: 3.76 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "light/night/search: 1.41 (needs 4.5) .cm-line \"Kept-xcoxud\"", + "dark/dawn/search: 3.40 (needs 4.5) .rt \"Kept-xcoxud\"", + "dark/dawn/search: 3.40 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "dark/dawn/search: 3.83 (needs 4.5) .rt \"Kept-mgq9su\"", + "dark/dawn/search: 3.83 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "dark/dawn/search: 3.88 (needs 4.5) .rt \"Kept-eveic1\"", + "dark/dawn/search: 3.88 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "dark/dawn/search: 3.83 (needs 4.5) .rt \"Kept-4v86y2\"", + "dark/dawn/search: 3.83 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "dark/dawn/search: 1.74 (needs 4.5) .cm-line \"Kept-xcoxud\"", + "dark/day/search: 3.38 (needs 4.5) .rt \"Kept-xcoxud\"", + "dark/day/search: 3.38 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "dark/day/search: 3.76 (needs 4.5) .rt \"Kept-mgq9su\"", + "dark/day/search: 3.76 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "dark/day/search: 3.86 (needs 4.5) .rt \"Kept-eveic1\"", + "dark/day/search: 3.86 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "dark/day/search: 3.88 (needs 4.5) .rt \"Kept-4v86y2\"", + "dark/day/search: 3.88 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "dark/day/search: 1.74 (needs 4.5) .cm-line \"Kept-xcoxud\"", + "dark/dusk/search: 3.43 (needs 4.5) .rt \"Kept-xcoxud\"", + "dark/dusk/search: 3.43 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "dark/dusk/search: 3.86 (needs 4.5) .rt \"Kept-mgq9su\"", + "dark/dusk/search: 3.86 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "dark/dusk/search: 3.93 (needs 4.5) .rt \"Kept-eveic1\"", + "dark/dusk/search: 3.93 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "dark/dusk/search: 3.93 (needs 4.5) .rt \"Kept-4v86y2\"", + "dark/dusk/search: 3.93 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "dark/dusk/search: 1.74 (needs 4.5) .cm-line \"Kept-xcoxud\"", + "dark/night/search: 3.54 (needs 4.5) .rt \"Kept-xcoxud\"", + "dark/night/search: 3.54 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "dark/night/search: 4.02 (needs 4.5) .rt \"Kept-mgq9su\"", + "dark/night/search: 4.02 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "dark/night/search: 4.02 (needs 4.5) .rt \"Kept-eveic1\"", + "dark/night/search: 4.02 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "dark/night/search: 4.02 (needs 4.5) .rt \"Kept-4v86y2\"", + "dark/night/search: 4.02 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "dark/night/search: 1.74 (needs 4.5) .cm-line \"Kept-xcoxud\"", + ] 53 | const finish = (report: string[], failures: string[], file?: string) => { 54 | if (file) writeFileSync(file, report.join('\n') + '\n') > 55 | expect(failures, failures.join('\n')).toEqual([]) | ^ 56 | } 57 | 58 | test('text and icons on the glass keep their contrast at every sky hour', async ({ page }) => { at finish (/home/claude/Developer/Worktrees/bm8/apps/web/e2e/contrast.spec.ts:55:41) at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/contrast.spec.ts:167:3 ``` ### 7. contrast · contrast.spec.ts:171 the palette keeps its contrast over a photo and over a page of huge words, 1440 wide First error ```text Error: 1440/light/photo/search: 3.64 (needs 4.5) .rt "Kept-xcoxud" 1440/light/photo/search: 3.64 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/light/photo/search: 3.87 (needs 4.5) .rt "Kept-dh9oq2" 1440/light/photo/search: 3.87 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/light/photo/search: 3.82 (needs 4.5) .rt "Kept-eveic1" 1440/light/photo/search: 3.82 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/light/photo/search: 3.75 (needs 4.5) .rt "Kept-mgq9su" 1440/light/photo/search: 3.75 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/light/photo/search: 3.68 (needs 4.5) .rt "Kept-4v86y2" 1440/light/photo/search: 3.68 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/light/photo/search: 1.41 (needs 4.5) .cm-line "Kept-xcoxud" 1440/light/photo + huge words/search: 3.35 (needs 4.5) .rt "Kept-xcoxud" 1440/light/photo + huge words/search: 3.35 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/light/photo + huge words/search: 3.51 (needs 4.5) .rt "Kept-dh9oq2" 1440/light/photo + huge words/search: 3.51 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/light/photo + huge words/search: 3.46 (needs 4.5) .rt "Kept-eveic1" 1440/light/photo + huge words/search: 3.46 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/light/photo + huge words/search: 3.39 (needs 4.5) .rt "Kept-mgq9su" 1440/light/photo + huge words/search: 3.39 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/light/photo + huge words/search: 3.34 (needs 4.5) .rt "Kept-4v86y2" 1440/light/photo + huge words/search: 3.34 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/light/photo + huge words/search: 1.73 (needs 4.5) .cm-line "Kept-xcoxud" 1440/light/photo + huge words/search: 1.73 (needs 4.5) .hit "Kept" 1440/dark/photo/search: 3.42 (needs 4.5) .rt "Kept-xcoxud" 1440/dark/photo/search: 3.42 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/dark/photo/search: 3.85 (needs 4.5) .rt "Kept-dh9oq2" 1440/dark/photo/search: 3.85 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/dark/photo/search: 3.85 (needs 4.5) .rt "Kept-eveic1" 1440/dark/photo/search: 3.85 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/dark/photo/search: 3.85 (needs 4.5) .rt "Kept-mgq9su" 1440/dark/photo/search: 3.85 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/dark/photo/search: 3.91 (needs 4.5) .rt "Kept-4v86y2" 1440/dark/photo/search: 3.91 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/dark/photo/search: 1.74 (needs 4.5) .cm-line "Kept-xcoxud" 1440/dark/photo + huge words/search: 3.32 (needs 4.5) .rt "Kept-xcoxud" 1440/dark/photo + huge words/search: 3.32 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/dark/photo + huge words/search: 3.65 (needs 4.5) .rt "Kept-dh9oq2" 1440/dark/photo + huge words/search: 3.65 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/dark/photo + huge words/search: 3.65 (needs 4.5) .rt "Kept-eveic1" 1440/dark/photo + huge words/search: 3.65 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/dark/photo + huge words/search: 3.69 (needs 4.5) .rt "Kept-mgq9su" 1440/dark/photo + huge words/search: 3.69 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/dark/photo + huge words/search: 3.69 (needs 4.5) .rt "Kept-4v86y2" 1440/dark/photo + huge words/search: 3.69 (needs 4.5) .svelte-1jp1e9h "Kept" 1440/dark/photo + huge words/search: 1.99 (needs 4.5) .cm-line "Kept-xcoxud" 1440/dark/photo + huge words/search: 1.99 (needs 4.5) .hit "Kept" expect(received).toEqual(expected) // deep equality - Expected - 1 + Received + 48 - Array [] + Array [ + "1440/light/photo/search: 3.64 (needs 4.5) .rt \"Kept-xcoxud\"", + "1440/light/photo/search: 3.64 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/light/photo/search: 3.87 (needs 4.5) .rt \"Kept-dh9oq2\"", + "1440/light/photo/search: 3.87 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/light/photo/search: 3.82 (needs 4.5) .rt \"Kept-eveic1\"", + "1440/light/photo/search: 3.82 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/light/photo/search: 3.75 (needs 4.5) .rt \"Kept-mgq9su\"", + "1440/light/photo/search: 3.75 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/light/photo/search: 3.68 (needs 4.5) .rt \"Kept-4v86y2\"", + "1440/light/photo/search: 3.68 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/light/photo/search: 1.41 (needs 4.5) .cm-line \"Kept-xcoxud\"", + "1440/light/photo + huge words/search: 3.35 (needs 4.5) .rt \"Kept-xcoxud\"", + "1440/light/photo + huge words/search: 3.35 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/light/photo + huge words/search: 3.51 (needs 4.5) .rt \"Kept-dh9oq2\"", + "1440/light/photo + huge words/search: 3.51 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/light/photo + huge words/search: 3.46 (needs 4.5) .rt \"Kept-eveic1\"", + "1440/light/photo + huge words/search: 3.46 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/light/photo + huge words/search: 3.39 (needs 4.5) .rt \"Kept-mgq9su\"", + "1440/light/photo + huge words/search: 3.39 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/light/photo + huge words/search: 3.34 (needs 4.5) .rt \"Kept-4v86y2\"", + "1440/light/photo + huge words/search: 3.34 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/light/photo + huge words/search: 1.73 (needs 4.5) .cm-line \"Kept-xcoxud\"", + "1440/light/photo + huge words/search: 1.73 (needs 4.5) .hit \"Kept\"", + "1440/dark/photo/search: 3.42 (needs 4.5) .rt \"Kept-xcoxud\"", + "1440/dark/photo/search: 3.42 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/dark/photo/search: 3.85 (needs 4.5) .rt \"Kept-dh9oq2\"", + "1440/dark/photo/search: 3.85 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/dark/photo/search: 3.85 (needs 4.5) .rt \"Kept-eveic1\"", + "1440/dark/photo/search: 3.85 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/dark/photo/search: 3.85 (needs 4.5) .rt \"Kept-mgq9su\"", + "1440/dark/photo/search: 3.85 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/dark/photo/search: 3.91 (needs 4.5) .rt \"Kept-4v86y2\"", + "1440/dark/photo/search: 3.91 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/dark/photo/search: 1.74 (needs 4.5) .cm-line \"Kept-xcoxud\"", + "1440/dark/photo + huge words/search: 3.32 (needs 4.5) .rt \"Kept-xcoxud\"", + "1440/dark/photo + huge words/search: 3.32 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/dark/photo + huge words/search: 3.65 (needs 4.5) .rt \"Kept-dh9oq2\"", + "1440/dark/photo + huge words/search: 3.65 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/dark/photo + huge words/search: 3.65 (needs 4.5) .rt \"Kept-eveic1\"", + "1440/dark/photo + huge words/search: 3.65 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/dark/photo + huge words/search: 3.69 (needs 4.5) .rt \"Kept-mgq9su\"", + "1440/dark/photo + huge words/search: 3.69 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/dark/photo + huge words/search: 3.69 (needs 4.5) .rt \"Kept-4v86y2\"", + "1440/dark/photo + huge words/search: 3.69 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "1440/dark/photo + huge words/search: 1.99 (needs 4.5) .cm-line \"Kept-xcoxud\"", + "1440/dark/photo + huge words/search: 1.99 (needs 4.5) .hit \"Kept\"", + ] 53 | const finish = (report: string[], failures: string[], file?: string) => { 54 | if (file) writeFileSync(file, report.join('\n') + '\n') > 55 | expect(failures, failures.join('\n')).toEqual([]) | ^ 56 | } 57 | 58 | test('text and icons on the glass keep their contrast at every sky hour', async ({ page }) => { at finish (/home/claude/Developer/Worktrees/bm8/apps/web/e2e/contrast.spec.ts:55:41) at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/contrast.spec.ts:186:5 ``` ### 8. contrast · contrast.spec.ts:171 the palette keeps its contrast over a photo and over a page of huge words, 390 wide First error ```text Error: 390/light/photo/search: 3.75 (needs 4.5) .rt "Kept-xcoxud" 390/light/photo/search: 3.75 (needs 4.5) .svelte-1jp1e9h "Kept" 390/light/photo/search: 4.03 (needs 4.5) .rt "Kept-eveic1" 390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept" 390/light/photo/search: 4.03 (needs 4.5) .rt "Kept-linv2c" 390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept" 390/light/photo/search: 4.03 (needs 4.5) .rt "Kept-4v86y2" 390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept" 390/light/photo/search: 4.03 (needs 4.5) .rt "Kept-dh9oq2" 390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept" 390/light/photo/search: 4.03 (needs 4.5) .rt "Kept-mgq9su" 390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept" 390/light/photo + huge words/search: 3.75 (needs 4.5) .rt "Kept-xcoxud" 390/light/photo + huge words/search: 3.75 (needs 4.5) .svelte-1jp1e9h "Kept" 390/light/photo + huge words/search: 4.03 (needs 4.5) .rt "Kept-eveic1" 390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept" 390/light/photo + huge words/search: 4.03 (needs 4.5) .rt "Kept-linv2c" 390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept" 390/light/photo + huge words/search: 4.03 (needs 4.5) .rt "Kept-4v86y2" 390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept" 390/light/photo + huge words/search: 4.03 (needs 4.5) .rt "Kept-dh9oq2" 390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept" 390/light/photo + huge words/search: 4.03 (needs 4.5) .rt "Kept-mgq9su" 390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h "Kept" 390/dark/photo/search: 3.67 (needs 4.5) .rt "Kept-xcoxud" 390/dark/photo/search: 3.67 (needs 4.5) .svelte-1jp1e9h "Kept" 390/dark/photo/search: 4.08 (needs 4.5) .rt "Kept-eveic1" 390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept" 390/dark/photo/search: 4.08 (needs 4.5) .rt "Kept-linv2c" 390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept" 390/dark/photo/search: 4.08 (needs 4.5) .rt "Kept-4v86y2" 390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept" 390/dark/photo/search: 4.08 (needs 4.5) .rt "Kept-dh9oq2" 390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept" 390/dark/photo/search: 4.08 (needs 4.5) .rt "Kept-mgq9su" 390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept" 390/dark/photo + huge words/search: 3.67 (needs 4.5) .rt "Kept-xcoxud" 390/dark/photo + huge words/search: 3.67 (needs 4.5) .svelte-1jp1e9h "Kept" 390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt "Kept-eveic1" 390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept" 390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt "Kept-linv2c" 390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept" 390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt "Kept-4v86y2" 390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept" 390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt "Kept-dh9oq2" 390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept" 390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt "Kept-mgq9su" 390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h "Kept" expect(received).toEqual(expected) // deep equality - Expected - 1 + Received + 50 - Array [] + Array [ + "390/light/photo/search: 3.75 (needs 4.5) .rt \"Kept-xcoxud\"", + "390/light/photo/search: 3.75 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/light/photo/search: 4.03 (needs 4.5) .rt \"Kept-eveic1\"", + "390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/light/photo/search: 4.03 (needs 4.5) .rt \"Kept-linv2c\"", + "390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/light/photo/search: 4.03 (needs 4.5) .rt \"Kept-4v86y2\"", + "390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/light/photo/search: 4.03 (needs 4.5) .rt \"Kept-dh9oq2\"", + "390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/light/photo/search: 4.03 (needs 4.5) .rt \"Kept-mgq9su\"", + "390/light/photo/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/light/photo + huge words/search: 3.75 (needs 4.5) .rt \"Kept-xcoxud\"", + "390/light/photo + huge words/search: 3.75 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/light/photo + huge words/search: 4.03 (needs 4.5) .rt \"Kept-eveic1\"", + "390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/light/photo + huge words/search: 4.03 (needs 4.5) .rt \"Kept-linv2c\"", + "390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/light/photo + huge words/search: 4.03 (needs 4.5) .rt \"Kept-4v86y2\"", + "390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/light/photo + huge words/search: 4.03 (needs 4.5) .rt \"Kept-dh9oq2\"", + "390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/light/photo + huge words/search: 4.03 (needs 4.5) .rt \"Kept-mgq9su\"", + "390/light/photo + huge words/search: 4.03 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/dark/photo/search: 3.67 (needs 4.5) .rt \"Kept-xcoxud\"", + "390/dark/photo/search: 3.67 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/dark/photo/search: 4.08 (needs 4.5) .rt \"Kept-eveic1\"", + "390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/dark/photo/search: 4.08 (needs 4.5) .rt \"Kept-linv2c\"", + "390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/dark/photo/search: 4.08 (needs 4.5) .rt \"Kept-4v86y2\"", + "390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/dark/photo/search: 4.08 (needs 4.5) .rt \"Kept-dh9oq2\"", + "390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/dark/photo/search: 4.08 (needs 4.5) .rt \"Kept-mgq9su\"", + "390/dark/photo/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/dark/photo + huge words/search: 3.67 (needs 4.5) .rt \"Kept-xcoxud\"", + "390/dark/photo + huge words/search: 3.67 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt \"Kept-eveic1\"", + "390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt \"Kept-linv2c\"", + "390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt \"Kept-4v86y2\"", + "390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt \"Kept-dh9oq2\"", + "390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + "390/dark/photo + huge words/search: 4.08 (needs 4.5) .rt \"Kept-mgq9su\"", + "390/dark/photo + huge words/search: 4.08 (needs 4.5) .svelte-1jp1e9h \"Kept\"", + ] 53 | const finish = (report: string[], failures: string[], file?: string) => { 54 | if (file) writeFileSync(file, report.join('\n') + '\n') > 55 | expect(failures, failures.join('\n')).toEqual([]) | ^ 56 | } 57 | 58 | test('text and icons on the glass keep their contrast at every sky hour', async ({ page }) => { at finish (/home/claude/Developer/Worktrees/bm8/apps/web/e2e/contrast.spec.ts:55:41) at /home/claude/Developer/Worktrees/bm8/apps/web/e2e/contrast.spec.ts:186:5 ```
Author
Owner

Final Chromium attribution: origin/dev 55f35fcde3bc91ca3781055577b93e93819f6cd4, one run of CI=1 CALTERNAL_BIN="$PWD/../../target/debug/calternal" E2E_DIST="$PWD/dist" bunx playwright test e2e/contrast.spec.ts --project=contrast --workers=1 --retries=0 --reporter=line,json: 9 passed, exit 0. All three corresponding train palette contrast failures passed on baseline. Thus all 8 Chromium failures are new/unresolved (not reproduced on baseline), 0 classified pre-existing. No spec/project baseline repeated.

Additional WebKit phone attribution: glass.spec.ts --project=webkit-390 4 passed, 2 skipped; lazy-styled.spec.ts --project=webkit-390 3 passed, 1 skipped; touch-select.spec.ts --project=webkit-390 16 passed. Matching train failures all passed on baseline. Commands used the same CI=1, binary/dist environment, workers=1 and retries=0.

WebKit has since reported a review4 sign-out timeout. Its one origin/dev review4-signout.spec.ts --project=webkit-390 run is in progress. Full train WebKit continues. Dev-01 own jobs are complete; cleanup starts there.

Final Chromium attribution: origin/dev `55f35fcde3bc91ca3781055577b93e93819f6cd4`, one run of `CI=1 CALTERNAL_BIN="$PWD/../../target/debug/calternal" E2E_DIST="$PWD/dist" bunx playwright test e2e/contrast.spec.ts --project=contrast --workers=1 --retries=0 --reporter=line,json`: **9 passed**, exit 0. All three corresponding train palette contrast failures passed on baseline. Thus all 8 Chromium failures are new/unresolved (not reproduced on baseline), 0 classified pre-existing. No spec/project baseline repeated. Additional WebKit phone attribution: `glass.spec.ts --project=webkit-390` 4 passed, 2 skipped; `lazy-styled.spec.ts --project=webkit-390` 3 passed, 1 skipped; `touch-select.spec.ts --project=webkit-390` 16 passed. Matching train failures all passed on baseline. Commands used the same CI=1, binary/dist environment, workers=1 and retries=0. WebKit has since reported a review4 sign-out timeout. Its one origin/dev `review4-signout.spec.ts --project=webkit-390` run is in progress. Full train WebKit continues. Dev-01 own jobs are complete; cleanup starts there.
Author
Owner

Origin/dev single-run sign-out attribution is complete. Command: CI=1 CALTERNAL_BIN="$PWD/../../target/debug/calternal" E2E_DIST="$PWD/dist" bunx playwright test e2e/review4-signout.spec.ts --project=webkit-390 --workers=1 --retries=0 --reporter=line,json. Baseline 55f35fcde3bc91ca3781055577b93e93819f6cd4: 9 passed, 1 failed, 1 skipped, exit 1. No repeats.

The exact train variant (stuck uplink=true, BroadcastChannel=true) passed on baseline. The baseline failed the neighboring stuck uplink=false, BroadcastChannel=true variant with the same 180-second timeout waiting for the Sign out warning. Thus this timeout family is pre-existing at the spec/signature level, but the exact train variant was not reproduced. This is not counted as a confirmed exact-case pre-existing match. All other observed train browser failures passed their matching baseline cases.

Baseline first errors (diagnostic run, not another train run):

webkit-390 · review4-signout.spec.ts:131

typing during logout is refused (stuck uplink=false, BroadcastChannel=true)

Test timeout of 180000ms exceeded.
Error: locator.waitFor: Target page, context or browser has been closed
Call log:
  - waiting for getByRole('dialog', { name: 'Sign out?' }) to be visible


  20 | async function logoutStartedOrWarning(page: Page, started: Promise<void>) {
  21 |   await Promise.race([started, (async () => {
> 22 |     await page.getByRole('dialog', { name: 'Sign out?' }).waitFor()
     |                                                           ^
  23 |     const summary = await page.evaluate(async () => {
  24 |       const markers = Object.keys(localStorage).filter((k) => k.startsWith('calternal-note-')).map((key) => ({
  25 |         key, wal: key.includes(':wal:') && localStorage.getItem(key) !== '[]',
    at /home/cua/Developer/Worktrees/bm8-base/apps/web/e2e/review4-signout.spec.ts:22:59
    at logoutStartedOrWarning (/home/cua/Developer/Worktrees/bm8-base/apps/web/e2e/review4-signout.spec.ts:65:4)
    at /home/cua/Developer/Worktrees/bm8-base/apps/web/e2e/review4-signout.spec.ts:197:13
Origin/dev single-run sign-out attribution is complete. Command: `CI=1 CALTERNAL_BIN="$PWD/../../target/debug/calternal" E2E_DIST="$PWD/dist" bunx playwright test e2e/review4-signout.spec.ts --project=webkit-390 --workers=1 --retries=0 --reporter=line,json`. Baseline `55f35fcde3bc91ca3781055577b93e93819f6cd4`: **9 passed, 1 failed, 1 skipped**, exit 1. No repeats. The exact train variant (`stuck uplink=true, BroadcastChannel=true`) **passed on baseline**. The baseline failed the neighboring `stuck uplink=false, BroadcastChannel=true` variant with the same 180-second timeout waiting for the Sign out warning. Thus this timeout family is pre-existing at the spec/signature level, but the exact train variant was not reproduced. This is not counted as a confirmed exact-case pre-existing match. All other observed train browser failures passed their matching baseline cases. Baseline first errors (diagnostic run, not another train run): ### webkit-390 · review4-signout.spec.ts:131 typing during logout is refused (stuck uplink=false, BroadcastChannel=true) ```text Test timeout of 180000ms exceeded. ``` ```text Error: locator.waitFor: Target page, context or browser has been closed Call log: - waiting for getByRole('dialog', { name: 'Sign out?' }) to be visible 20 | async function logoutStartedOrWarning(page: Page, started: Promise<void>) { 21 | await Promise.race([started, (async () => { > 22 | await page.getByRole('dialog', { name: 'Sign out?' }).waitFor() | ^ 23 | const summary = await page.evaluate(async () => { 24 | const markers = Object.keys(localStorage).filter((k) => k.startsWith('calternal-note-')).map((key) => ({ 25 | key, wal: key.includes(':wal:') && localStorage.getItem(key) !== '[]', at /home/cua/Developer/Worktrees/bm8-base/apps/web/e2e/review4-signout.spec.ts:22:59 at logoutStartedOrWarning (/home/cua/Developer/Worktrees/bm8-base/apps/web/e2e/review4-signout.spec.ts:65:4) at /home/cua/Developer/Worktrees/bm8-base/apps/web/e2e/review4-signout.spec.ts:197:13 ```
Author
Owner

Webkit full first-run failure catalog. Train head 4d02e704a281dda00655f45aca6d0d6744fb6357. No retries.

1. webkit-390 · glass.spec.ts:341

every floating surface is the one glass

First error

Error: search: the bar's shade, edge and shadow

expect(received).toBe(expected) // Object.is equality

Expected: "rgba(16, 15, 14, 0.3) 0px 0px 20px 0px inset, rgba(255, 255, 255, 0.1) 0px 0px 0px 1px inset, rgba(255, 255, 255, 0.14) 0px 1px 0px 0px inset, rgba(255, 255, 255, 0.08) 0px 0px 0px 1px, rgba(0, 0, 0, 0.55) 0px 12px 30px -10px"
Received: "rgba(16, 15, 14, 0.3) 0px 0px 20px 0px inset, rgba(255, 255, 255, 0.1) 0px 0px 0px 1px inset, rgba(255, 255, 255, 0.14) 0px 1px 0px 0px inset"

  369 |   const same = (m: typeof bar, dense: boolean, what: string) => {
  370 |     expect(m.filter, `${what}: the bar's blur`).toBe(bar.filter)
> 371 |     expect(m.shadow, `${what}: the bar's shade, edge and shadow`).toBe(bar.shadow)
      |                                                                   ^
  372 |     expect(m.kids, `${what}: its glass holds nothing`).toBe(0)
  373 |     return tint(dense).then((t) => expect(m.bg, `${what}: its tint`).toBe(t))
  374 |   }
    at same (/home/cua/Developer/Worktrees/bm8/apps/web/e2e/glass.spec.ts:371:67)
    at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/glass.spec.ts:379:9

2. webkit-390 · lazy-styled.spec.ts:54

lazily loaded sheets, search and menu are styled on their first frame

First error

Error: search: unstyled on its first frame

expect(received).toEqual(expected) // deep equality

- Expected  - 1
+ Received  + 4

- Array []
+ Array [
+   "BUTTON.kopt press svelte-3pqrsc fontWeight: 620 -> 500",
+   "BUTTON.kopt press svelte-3pqrsc fontWeight: 500 -> 620",
+ ]

  82 |   const search = await flashes(page, '.palette')
  83 |   expect(search.seen).toBeGreaterThan(3)
> 84 |   expect(search.bad, 'search: unstyled on its first frame').toEqual([])
     |                                                             ^
  85 |   await page.keyboard.press('Escape')
  86 |
  87 |   // The shared menu also loads its styles before its first open.
    at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/lazy-styled.spec.ts:84:61

3. webkit-1440 · preload.spec.ts:111

palette Control intent prepares only its own screen before idle

First error

Error: opening the prepared palette needs no new scripts

expect(received).toHaveLength(expected)

Expected length: 4
Received length: 5
Received array:  ["/assets/index-BR4nh5B0.js", "/assets/rolldown-runtime-BzPIFoFC.js", "/assets/two-finger-CkCPX5CV.js", "/assets/Palette-Duz6PTlQ.js", "/assets/index-BR4nh5B0.js"]

  132 |     await page.keyboard.press('Control+k')
  133 |     await expect(page.locator('.palette [role="combobox"]')).toBeVisible()
> 134 |     expect(scripts, 'opening the prepared palette needs no new scripts').toHaveLength(ready)
      |                                                                          ^
  135 |   })
  136 | }
  137 |
    at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/preload.spec.ts:134:74

4. webkit-1440 · preload.spec.ts:111

palette Meta intent prepares only its own screen before idle

First error

Error: opening the prepared palette needs no new scripts

expect(received).toHaveLength(expected)

Expected length: 4
Received length: 5
Received array:  ["/assets/index-BR4nh5B0.js", "/assets/rolldown-runtime-BzPIFoFC.js", "/assets/two-finger-CkCPX5CV.js", "/assets/Palette-Duz6PTlQ.js", "/assets/index-BR4nh5B0.js"]

  132 |     await page.keyboard.press('Control+k')
  133 |     await expect(page.locator('.palette [role="combobox"]')).toBeVisible()
> 134 |     expect(scripts, 'opening the prepared palette needs no new scripts').toHaveLength(ready)
      |                                                                          ^
  135 |   })
  136 | }
  137 |
    at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/preload.spec.ts:134:74

5. webkit-390 · review4-signout.spec.ts:120

typing during logout is refused (stuck uplink=true, BroadcastChannel=true)

First error

Test timeout of 180000ms exceeded.

Additional error from the same first attempt

Error: locator.waitFor: Target page, context or browser has been closed
Call log:
  - waiting for getByRole('dialog', { name: 'Sign out?' }) to be visible


   9 | async function logoutStartedOrWarning(page: Page, started: Promise<void>) {
  10 |   await Promise.race([started, (async () => {
> 11 |     await page.getByRole('dialog', { name: 'Sign out?' }).waitFor()
     |                                                           ^
  12 |     const summary = await page.evaluate(async () => {
  13 |       const markers = Object.keys(localStorage).filter((k) => k.startsWith('calternal-note-')).map((key) => ({
  14 |         key, wal: key.includes(':wal:') && localStorage.getItem(key) !== '[]',
    at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/review4-signout.spec.ts:11:59
    at logoutStartedOrWarning (/home/cua/Developer/Worktrees/bm8/apps/web/e2e/review4-signout.spec.ts:54:4)
    at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/review4-signout.spec.ts:193:13

6. webkit-1440 · smart-note.spec.ts:19

→ on a log line makes a note with its times and tags, and Undo takes it back

First error

Error: expect(page).toHaveURL(expected) failed

Expected pattern: /day-2068-12-10/
Received string:  "http://127.0.0.1:35159/?test=1#/note/01a19c91-f06d-7014-a0b7-db00b4556a65?list=journal"
Timeout: 5000ms

Call log:
  - Expect "toHaveURL" with timeout 5000ms
    14 × locator resolved to <html class="" lang="en" data-sky="dusk" data-app="ready" data-ready="true" data-scheme="dark" data-listed="true" data-synced="true" data-backdrop="sky" data-input="pointer" data-accent="marigold" data-last-input="pointer">…</html>
       - unexpected value "http://127.0.0.1:35159/?test=1#/note/01a19c91-f06d-7014-a0b7-db00b4556a65?list=journal"


  52 |   // One Undo: the note goes and the line comes back exactly.
  53 |   await page.getByRole('button', { name: 'Undo' }).click()
> 54 |   await expect(page).toHaveURL(new RegExp(`day-${iso}`))
     |                      ^
  55 |   await expect.poll(async () => body((await get(`day-${iso}`)).text), { timeout: 15_000 }).toBe(before)
  56 |   // (The editor shows the bullet as a dot.)
  57 |   await expect(page.getByRole('textbox', { name: 'Note text' })).toContainText(`09:30 - 10:30 ${words} #area/abc`)
    at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/smart-note.spec.ts:54:22

7. webkit-390 · touch-select.spec.ts:140

a scroll stays a scroll

First error

Error: expect(received).toBe(expected) // Object.is equality

Expected: true
Received: false

  145 |     { type: 'touchend', x: x + 12, y: y + 90 },
  146 |   ])
> 147 |   expect(prevented.every((p) => !p)).toBe(true)
      |                                      ^
  148 |   await expect(blockBar(page)).toHaveCount(0)
  149 | })
  150 |
    at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/touch-select.spec.ts:147:38

Every entry is the first attempt. Baseline attribution is in separate comments and the final table.

Webkit full first-run failure catalog. Train head `4d02e704a281dda00655f45aca6d0d6744fb6357`. No retries. ### 1. webkit-390 · glass.spec.ts:341 every floating surface is the one glass First error ```text Error: search: the bar's shade, edge and shadow expect(received).toBe(expected) // Object.is equality Expected: "rgba(16, 15, 14, 0.3) 0px 0px 20px 0px inset, rgba(255, 255, 255, 0.1) 0px 0px 0px 1px inset, rgba(255, 255, 255, 0.14) 0px 1px 0px 0px inset, rgba(255, 255, 255, 0.08) 0px 0px 0px 1px, rgba(0, 0, 0, 0.55) 0px 12px 30px -10px" Received: "rgba(16, 15, 14, 0.3) 0px 0px 20px 0px inset, rgba(255, 255, 255, 0.1) 0px 0px 0px 1px inset, rgba(255, 255, 255, 0.14) 0px 1px 0px 0px inset" 369 | const same = (m: typeof bar, dense: boolean, what: string) => { 370 | expect(m.filter, `${what}: the bar's blur`).toBe(bar.filter) > 371 | expect(m.shadow, `${what}: the bar's shade, edge and shadow`).toBe(bar.shadow) | ^ 372 | expect(m.kids, `${what}: its glass holds nothing`).toBe(0) 373 | return tint(dense).then((t) => expect(m.bg, `${what}: its tint`).toBe(t)) 374 | } at same (/home/cua/Developer/Worktrees/bm8/apps/web/e2e/glass.spec.ts:371:67) at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/glass.spec.ts:379:9 ``` ### 2. webkit-390 · lazy-styled.spec.ts:54 lazily loaded sheets, search and menu are styled on their first frame First error ```text Error: search: unstyled on its first frame expect(received).toEqual(expected) // deep equality - Expected - 1 + Received + 4 - Array [] + Array [ + "BUTTON.kopt press svelte-3pqrsc fontWeight: 620 -> 500", + "BUTTON.kopt press svelte-3pqrsc fontWeight: 500 -> 620", + ] 82 | const search = await flashes(page, '.palette') 83 | expect(search.seen).toBeGreaterThan(3) > 84 | expect(search.bad, 'search: unstyled on its first frame').toEqual([]) | ^ 85 | await page.keyboard.press('Escape') 86 | 87 | // The shared menu also loads its styles before its first open. at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/lazy-styled.spec.ts:84:61 ``` ### 3. webkit-1440 · preload.spec.ts:111 palette Control intent prepares only its own screen before idle First error ```text Error: opening the prepared palette needs no new scripts expect(received).toHaveLength(expected) Expected length: 4 Received length: 5 Received array: ["/assets/index-BR4nh5B0.js", "/assets/rolldown-runtime-BzPIFoFC.js", "/assets/two-finger-CkCPX5CV.js", "/assets/Palette-Duz6PTlQ.js", "/assets/index-BR4nh5B0.js"] 132 | await page.keyboard.press('Control+k') 133 | await expect(page.locator('.palette [role="combobox"]')).toBeVisible() > 134 | expect(scripts, 'opening the prepared palette needs no new scripts').toHaveLength(ready) | ^ 135 | }) 136 | } 137 | at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/preload.spec.ts:134:74 ``` ### 4. webkit-1440 · preload.spec.ts:111 palette Meta intent prepares only its own screen before idle First error ```text Error: opening the prepared palette needs no new scripts expect(received).toHaveLength(expected) Expected length: 4 Received length: 5 Received array: ["/assets/index-BR4nh5B0.js", "/assets/rolldown-runtime-BzPIFoFC.js", "/assets/two-finger-CkCPX5CV.js", "/assets/Palette-Duz6PTlQ.js", "/assets/index-BR4nh5B0.js"] 132 | await page.keyboard.press('Control+k') 133 | await expect(page.locator('.palette [role="combobox"]')).toBeVisible() > 134 | expect(scripts, 'opening the prepared palette needs no new scripts').toHaveLength(ready) | ^ 135 | }) 136 | } 137 | at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/preload.spec.ts:134:74 ``` ### 5. webkit-390 · review4-signout.spec.ts:120 typing during logout is refused (stuck uplink=true, BroadcastChannel=true) First error ```text Test timeout of 180000ms exceeded. ``` Additional error from the same first attempt ```text Error: locator.waitFor: Target page, context or browser has been closed Call log: - waiting for getByRole('dialog', { name: 'Sign out?' }) to be visible 9 | async function logoutStartedOrWarning(page: Page, started: Promise<void>) { 10 | await Promise.race([started, (async () => { > 11 | await page.getByRole('dialog', { name: 'Sign out?' }).waitFor() | ^ 12 | const summary = await page.evaluate(async () => { 13 | const markers = Object.keys(localStorage).filter((k) => k.startsWith('calternal-note-')).map((key) => ({ 14 | key, wal: key.includes(':wal:') && localStorage.getItem(key) !== '[]', at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/review4-signout.spec.ts:11:59 at logoutStartedOrWarning (/home/cua/Developer/Worktrees/bm8/apps/web/e2e/review4-signout.spec.ts:54:4) at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/review4-signout.spec.ts:193:13 ``` ### 6. webkit-1440 · smart-note.spec.ts:19 → on a log line makes a note with its times and tags, and Undo takes it back First error ```text Error: expect(page).toHaveURL(expected) failed Expected pattern: /day-2068-12-10/ Received string: "http://127.0.0.1:35159/?test=1#/note/01a19c91-f06d-7014-a0b7-db00b4556a65?list=journal" Timeout: 5000ms Call log: - Expect "toHaveURL" with timeout 5000ms 14 × locator resolved to <html class="" lang="en" data-sky="dusk" data-app="ready" data-ready="true" data-scheme="dark" data-listed="true" data-synced="true" data-backdrop="sky" data-input="pointer" data-accent="marigold" data-last-input="pointer">…</html> - unexpected value "http://127.0.0.1:35159/?test=1#/note/01a19c91-f06d-7014-a0b7-db00b4556a65?list=journal" 52 | // One Undo: the note goes and the line comes back exactly. 53 | await page.getByRole('button', { name: 'Undo' }).click() > 54 | await expect(page).toHaveURL(new RegExp(`day-${iso}`)) | ^ 55 | await expect.poll(async () => body((await get(`day-${iso}`)).text), { timeout: 15_000 }).toBe(before) 56 | // (The editor shows the bullet as a dot.) 57 | await expect(page.getByRole('textbox', { name: 'Note text' })).toContainText(`09:30 - 10:30 ${words} #area/abc`) at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/smart-note.spec.ts:54:22 ``` ### 7. webkit-390 · touch-select.spec.ts:140 a scroll stays a scroll First error ```text Error: expect(received).toBe(expected) // Object.is equality Expected: true Received: false 145 | { type: 'touchend', x: x + 12, y: y + 90 }, 146 | ]) > 147 | expect(prevented.every((p) => !p)).toBe(true) | ^ 148 | await expect(blockBar(page)).toHaveCount(0) 149 | }) 150 | at /home/cua/Developer/Worktrees/bm8/apps/web/e2e/touch-select.spec.ts:147:38 ``` Every entry is the first attempt. Baseline attribution is in separate comments and the final table.
Author
Owner

Each failed train case was checked in one origin/dev invocation of the same spec/project. No spec/project baseline was repeated. Baseline head 55f35fcde3bc91ca3781055577b93e93819f6cd4; only #779 Nix tooling was overlaid, uncommitted.

Train project Spec Failed train case Matching origin/dev case Attribution
chromium-390 glass.spec.ts:341 every floating surface is the one glass PASS new/unresolved; not reproduced
chromium-390 lazy-styled.spec.ts:54 lazily loaded sheets, search and menu are styled on their first frame PASS new/unresolved; not reproduced
chromium-1440 search-corpus.spec.ts:44 every prefix, the full word, one-letter and two-letter typos find their note PASS new/unresolved; not reproduced
chromium-390 touch-select.spec.ts:140 a scroll stays a scroll PASS new/unresolved; not reproduced
chromium-1440 vim.spec.ts:341 / searches in normal mode, in the bar; in insert mode it is a slash PASS new/unresolved; not reproduced
contrast contrast.spec.ts:155 the palette keeps its contrast at every sky hour PASS new/unresolved; not reproduced
contrast contrast.spec.ts:171 the palette keeps its contrast over a photo and over a page of huge words, 1440 wide PASS new/unresolved; not reproduced
contrast contrast.spec.ts:171 the palette keeps its contrast over a photo and over a page of huge words, 390 wide PASS new/unresolved; not reproduced
webkit-390 glass.spec.ts:341 every floating surface is the one glass PASS new/unresolved; not reproduced
webkit-390 lazy-styled.spec.ts:54 lazily loaded sheets, search and menu are styled on their first frame PASS new/unresolved; not reproduced
webkit-1440 preload.spec.ts:111 palette Control intent prepares only its own screen before idle PASS new/unresolved; not reproduced
webkit-1440 preload.spec.ts:111 palette Meta intent prepares only its own screen before idle PASS new/unresolved; not reproduced
webkit-390 review4-signout.spec.ts:120 typing during logout is refused (stuck uplink=true, BroadcastChannel=true) PASS timeout family reproduced in a different parameter variant; exact variant unconfirmed
webkit-1440 smart-note.spec.ts:19 → on a log line makes a note with its times and tags, and Undo takes it back PASS new/unresolved; not reproduced
webkit-390 touch-select.spec.ts:140 a scroll stays a scroll PASS new/unresolved; not reproduced

All 15 exact failed train cases passed their matching baseline cases. The baseline sign-out spec also failed a different parameter variant: stuck uplink=false, BroadcastChannel=true; the train failed stuck uplink=true, BroadcastChannel=true. Both hit the 180-second wait for the Sign out warning, so the timeout family is pre-existing at spec/signature level, while exact-variant reproduction remains unconfirmed. It does not establish that every train failure is pre-existing. The full baseline error is in the diagnostic comment.

Baseline invocations (all with CI=1, CALTERNAL_BIN/E2E_DIST pointing at the origin/dev build, Nix + slot):

Project Exact test command in apps/web Passed Failed Skipped
chromium-1440 bunx playwright test e2e/search-corpus.spec.ts --project=chromium-1440 --workers=1 --retries=0 --reporter=line,json 2 0 0
webkit-1440 bunx playwright test e2e/preload.spec.ts --project=webkit-1440 --workers=1 --retries=0 --reporter=line,json 9 0 3
chromium-1440 bunx playwright test e2e/vim.spec.ts --project=chromium-1440 --workers=1 --retries=0 --reporter=line,json 13 0 1
webkit-1440 bunx playwright test e2e/smart-note.spec.ts --project=webkit-1440 --workers=1 --retries=0 --reporter=line,json 1 0 0
chromium-390 bunx playwright test e2e/glass.spec.ts --project=chromium-390 --workers=1 --retries=0 --reporter=line,json 4 0 2
chromium-390 bunx playwright test e2e/lazy-styled.spec.ts --project=chromium-390 --workers=1 --retries=0 --reporter=line,json 3 0 1
webkit-390 bunx playwright test e2e/glass.spec.ts --project=webkit-390 --workers=1 --retries=0 --reporter=line,json 4 0 2
webkit-390 bunx playwright test e2e/lazy-styled.spec.ts --project=webkit-390 --workers=1 --retries=0 --reporter=line,json 3 0 1
chromium-390 bunx playwright test e2e/touch-select.spec.ts --project=chromium-390 --workers=1 --retries=0 --reporter=line,json 16 0 0
contrast bunx playwright test e2e/contrast.spec.ts --project=contrast --workers=1 --retries=0 --reporter=line,json 9 0 0
webkit-390 bunx playwright test e2e/touch-select.spec.ts --project=webkit-390 --workers=1 --retries=0 --reporter=line,json 16 0 0
webkit-390 bunx playwright test e2e/review4-signout.spec.ts --project=webkit-390 --workers=1 --retries=0 --reporter=line,json 9 1 1
Each failed train case was checked in one origin/dev invocation of the same spec/project. No spec/project baseline was repeated. Baseline head `55f35fcde3bc91ca3781055577b93e93819f6cd4`; only #779 Nix tooling was overlaid, uncommitted. | Train project | Spec | Failed train case | Matching origin/dev case | Attribution | |---|---|---|---|---| | chromium-390 | `glass.spec.ts:341` | every floating surface is the one glass | PASS | new/unresolved; not reproduced | | chromium-390 | `lazy-styled.spec.ts:54` | lazily loaded sheets, search and menu are styled on their first frame | PASS | new/unresolved; not reproduced | | chromium-1440 | `search-corpus.spec.ts:44` | every prefix, the full word, one-letter and two-letter typos find their note | PASS | new/unresolved; not reproduced | | chromium-390 | `touch-select.spec.ts:140` | a scroll stays a scroll | PASS | new/unresolved; not reproduced | | chromium-1440 | `vim.spec.ts:341` | / searches in normal mode, in the bar; in insert mode it is a slash | PASS | new/unresolved; not reproduced | | contrast | `contrast.spec.ts:155` | the palette keeps its contrast at every sky hour | PASS | new/unresolved; not reproduced | | contrast | `contrast.spec.ts:171` | the palette keeps its contrast over a photo and over a page of huge words, 1440 wide | PASS | new/unresolved; not reproduced | | contrast | `contrast.spec.ts:171` | the palette keeps its contrast over a photo and over a page of huge words, 390 wide | PASS | new/unresolved; not reproduced | | webkit-390 | `glass.spec.ts:341` | every floating surface is the one glass | PASS | new/unresolved; not reproduced | | webkit-390 | `lazy-styled.spec.ts:54` | lazily loaded sheets, search and menu are styled on their first frame | PASS | new/unresolved; not reproduced | | webkit-1440 | `preload.spec.ts:111` | palette Control intent prepares only its own screen before idle | PASS | new/unresolved; not reproduced | | webkit-1440 | `preload.spec.ts:111` | palette Meta intent prepares only its own screen before idle | PASS | new/unresolved; not reproduced | | webkit-390 | `review4-signout.spec.ts:120` | typing during logout is refused (stuck uplink=true, BroadcastChannel=true) | PASS | timeout family reproduced in a different parameter variant; exact variant unconfirmed | | webkit-1440 | `smart-note.spec.ts:19` | → on a log line makes a note with its times and tags, and Undo takes it back | PASS | new/unresolved; not reproduced | | webkit-390 | `touch-select.spec.ts:140` | a scroll stays a scroll | PASS | new/unresolved; not reproduced | All **15 exact failed train cases passed** their matching baseline cases. The baseline sign-out spec also failed a different parameter variant: `stuck uplink=false, BroadcastChannel=true`; the train failed `stuck uplink=true, BroadcastChannel=true`. Both hit the 180-second wait for the Sign out warning, so the timeout family is pre-existing at spec/signature level, while exact-variant reproduction remains unconfirmed. It does not establish that every train failure is pre-existing. The full baseline error is in [the diagnostic comment](https://git.kayg.org/kayg/calternal/pulls/862#issuecomment-75173). Baseline invocations (all with `CI=1`, `CALTERNAL_BIN`/`E2E_DIST` pointing at the origin/dev build, Nix + slot): | Project | Exact test command in apps/web | Passed | Failed | Skipped | |---|---|---:|---:|---:| | chromium-1440 | `bunx playwright test e2e/search-corpus.spec.ts --project=chromium-1440 --workers=1 --retries=0 --reporter=line,json` | 2 | 0 | 0 | | webkit-1440 | `bunx playwright test e2e/preload.spec.ts --project=webkit-1440 --workers=1 --retries=0 --reporter=line,json` | 9 | 0 | 3 | | chromium-1440 | `bunx playwright test e2e/vim.spec.ts --project=chromium-1440 --workers=1 --retries=0 --reporter=line,json` | 13 | 0 | 1 | | webkit-1440 | `bunx playwright test e2e/smart-note.spec.ts --project=webkit-1440 --workers=1 --retries=0 --reporter=line,json` | 1 | 0 | 0 | | chromium-390 | `bunx playwright test e2e/glass.spec.ts --project=chromium-390 --workers=1 --retries=0 --reporter=line,json` | 4 | 0 | 2 | | chromium-390 | `bunx playwright test e2e/lazy-styled.spec.ts --project=chromium-390 --workers=1 --retries=0 --reporter=line,json` | 3 | 0 | 1 | | webkit-390 | `bunx playwright test e2e/glass.spec.ts --project=webkit-390 --workers=1 --retries=0 --reporter=line,json` | 4 | 0 | 2 | | webkit-390 | `bunx playwright test e2e/lazy-styled.spec.ts --project=webkit-390 --workers=1 --retries=0 --reporter=line,json` | 3 | 0 | 1 | | chromium-390 | `bunx playwright test e2e/touch-select.spec.ts --project=chromium-390 --workers=1 --retries=0 --reporter=line,json` | 16 | 0 | 0 | | contrast | `bunx playwright test e2e/contrast.spec.ts --project=contrast --workers=1 --retries=0 --reporter=line,json` | 9 | 0 | 0 | | webkit-390 | `bunx playwright test e2e/touch-select.spec.ts --project=webkit-390 --workers=1 --retries=0 --reporter=line,json` | 16 | 0 | 0 | | webkit-390 | `bunx playwright test e2e/review4-signout.spec.ts --project=webkit-390 --workers=1 --retries=0 --reporter=line,json` | 9 | 1 | 1 |
Author
Owner

Final verdict: NOT GREEN. Never merged.

Train BM8: #862 into dev, branch train/2026-11-02-bm8, head 4d02e704a281dda00655f45aca6d0d6744fb6357, assembled on origin/dev 55f35fcde3bc91ca3781055577b93e93819f6cd4. BM7 #838 is closed with a replacement link. All 16 requested PRs included; none excluded.

Included source heads captured at assembly time, in ordered --no-ff merges:

PR Current head at assembly Merge commit
#779 7b97e4d14231be07f6a80cb9ca172d02442c487e f8931e0d3081da14f79b5639e33d16c5591b565b
#858 9c05d871a1692df46eb9e06e0e386bdf8c0bbd9c ac1a23dde8ab32f7033d5b58194b7d25caa99a57
#824 f1c1aa961e169ef99e132063a97ccbd9a89a5def 6e5e659044873b52d95cdca6ee6d4df765ef6a0c
#827 91a19dcf0a9cf3844861e0e6acf4318618f3ea33 c3a626df5418c3b5a1d4e73e4965b0bede7ab925
#829 d455842942731aabc05474c7929597d57090a7cc 807d3b6955df8b6e972fe897f9240cac2e0c4c85
#835 19071be8455d718822ba98f6b9a402c5fd345242 f8268ea5f1be46a2aef2ce04efa83c6c0adcd0fe
#828 5d7b9b36cd008de78851809724c90a85d92b40a7 d3f297b719c4ab851aee749d3a0e3f3737e3624a
#833 ccaece3dfecbe2048634fc792d7d4a2c6e31f324 b2a141ea1553ddd8c8a1de197fb3bd2aa686f60e
#836 5f7d1a59776411e7f3353d0023817b43affae51e 7959619d8ecf3c885d877dc3557cd52fd8eefbb6
#831 316b43e9578c3e4d2944df8df43caebc611356f7 110abecb403bfc6b9e8fd9654a46892c55f62931
#844 50cce9dcceb87f23a9f346300b3336e315ff6c0c 9abec9665d4009563edf380e4ae688c1ea68857e
#847 40448327a15730271d56226b898e48bcc87b2a4d 49ff52c50f6bd9e750c416924783eec7e95a1932
#855 c38c3083d51f4adcf5cd76a70f1f6960b427413e 00b4107c2d713204d8b09ac5f4ed54ec77077aad
#857 144e717145e35f70356f593b7c04a527e00f7b17 02e53ae075e78e4b49be7881459a7ab59f4097ce
#856 85845e3d11bea351608bde7481779ba2303372e5 09bd556a905eac10bb1e4232655220f1b1c2429f
#840 b68ace95fae5fb05a17697a667f69b020630d136 0cbcc5411190f48a955b0bd36aa7cd4aaeacf954

Mechanical resolutions only: Cargo.lock retains both IMAP UID and markdown-view packages in name order; execute.rs retains the app-password capability/scope guard and starts runtime timing immediately after it. #827 includes 91a19dcf; #828 includes fe46caf5. The final cleanup commit moves #831 measurement artifacts into a PR comment; no implementation fixes were made during train validation.

First-run gates, all with CI=1, Nix shell including strace, heavy steps through ~/bin/slot:

Exact command Result
cargo fmt --all --check PASS
cargo clippy --workspace --all-targets --locked -j4 -- -D warnings PASS
cargo test --workspace -j4 PASS, complete workspace including doc tests; deterministic long-note crash test ran and passed
cargo build -j4 PASS on both VMs
bun run build (apps/web) PASS on both VMs
PERF_DIST="$PWD/apps/web/dist" PERF_OUT="$HOME/bm8-train/bundle" PERF_WORK="$HOME/bm8-train/bundle-work" bash perf/web.sh --bundle-only --gate FAIL (1): total JS Brotli 324,932 bytes / 317.316 KiB > 315 KiB; origin/dev same command PASS, 319,370 bytes / 311.885 KiB
node ci/check-case-paths.mjs PASS
node ci/check-no-evidence.mjs FAIL (1): product source apps/web/src/composer/ResultsList.svelte falsely matches results; origin/dev lacks this script, so the same command gives module-not-found, not this failure

The bundle excess is 2,372 bytes over budget. First-paint JS (235.316 KiB < 240 KiB) and opt-in plugin JS (37.894 KiB < 45 KiB) pass. The two gate first errors are in the gate comment, with one-run baseline results. The evidence-guard failure is a new integration false positive, not an actual captured artifact.

Full first Playwright suite, Chromium/dev-01 and WebKit/dev-02 released together at 19:10:19 IST and 19:10:18 IST, respectively:

Half Selected Passed Failed Skipped Did not run Exit
Chromium/dev-01 2134 1338 8 781 7 1
WebKit/dev-02 2113 1312 7 794 0 1
Total 4247 2650 15 1575 7

Both admin-setup cases passed, each in the same invocation as its dependent admin projects. Both JSON reports have zero flaky results, zero global reporter errors and at most one attempt per test. The four Chromium throttled cases were blocked by main-project failures; three sidebar-band-chromium cases were blocked by contrast failures. No blocked case was rerun. Native @perf/SHOTS_DIR and platform skips remain visible in the table.

Host Project Passed Failed Skipped Did not run
dev-01 chromium-1440 424 2 602 0
dev-01 chromium-390 864 3 161 0
dev-01 throttled-1440 0 0 0 2
dev-01 throttled-390 0 0 0 2
dev-01 admin-chromium-1440 12 0 9 0
dev-01 admin-chromium-390 12 0 9 0
dev-01 flow 1 0 0 0
dev-01 passkeys 1 0 0 0
dev-01 admin-setup 1 0 0 0
dev-01 prefs-chromium 7 0 0 0
dev-01 look-chromium-1440 5 0 0 0
dev-01 look-chromium-390 5 0 0 0
dev-01 contrast 6 3 0 0
dev-01 sidebar-band-chromium 0 0 0 3
dev-02 webkit-1440 418 3 607 0
dev-02 webkit-390 861 4 163 0
dev-02 admin-webkit-1440 9 0 9 0
dev-02 admin-webkit-390 9 0 9 0
dev-02 admin-setup 1 0 0 0
dev-02 prefs-webkit 3 0 4 0
dev-02 look-webkit-1440 4 0 1 0
dev-02 look-webkit-390 4 0 1 0
dev-02 sidebar-band-webkit 3 0 0 0

Every first error is preserved in the complete catalogs: all 8 Chromium failures, all 7 WebKit failures.

Per-failure baseline attribution and all 12 exact baseline commands: all 15 exact failed train cases passed their matching origin/dev cases. Fourteen are new/unresolved with no baseline failure signature. The sign-out timeout family also occurred on origin/dev, but in stuck uplink=false, BroadcastChannel=true; the train failed the true,true variant, which passed on baseline. The family is pre-existing at the spec/signature level; an exact-variant match is unconfirmed. The complete baseline first errors are posted separately. This does not establish every failure as pre-existing, so the verdict cannot be green. No failure was excused merely because another case in the same file failed.

Exact full-browser command in apps/web (WebKit uses BM8_HALF=webkit):

export CI=1 BM8_HALF=chromium
export CALTERNAL_BIN="$PWD/../../target/debug/calternal" E2E_DIST="$PWD/dist"
export TMPDIR="$HOME/bm8-train/browser-tmp"
export PLAYWRIGHT_JSON_OUTPUT_NAME="$HOME/bm8-train/playwright.json"
bunx playwright test --config=playwright.bm8.config.ts --output="$HOME/bm8-train/test-results" --workers=1 --retries=0 --reporter=line,json

The throwaway split config imports the native config, retains all non-WebKit projects on dev-01, and WebKit projects plus admin-setup on dev-02. Only dependencies assigned to the other half are removed; same-half dependencies remain. Its contents were:

import { defineConfig } from '@playwright/test'
import base from './playwright.config'
const webkit = process.env.BM8_HALF === 'webkit'
const projects = (base.projects ?? []).filter(p => p.name === 'admin-setup' || (webkit ? p.name?.includes('webkit') : !p.name?.includes('webkit')))
const names = new Set(projects.map(p => p.name))
export default defineConfig({ ...base, projects: projects.map(p => ({ ...p, dependencies: p.dependencies?.filter(d => names.has(d)) })) })

Preparation: bun install --frozen-lockfile at each worktree root. Rust used CARGO_PROFILE_DEV_DEBUG=0 CARGO_PROFILE_TEST_DEBUG=0 CARGO_INCREMENTAL=0. Detached jobs used 10800-second unit limits and ~/bin/slot /nix/var/nix/profiles/default/bin/nix develop -c bash <script>. Dev-02 ran binary/web prerequisites only, then its full browser half. No build or test ran on the control host. The initial Nix working-directory setup error occurred before any gate executed and is explicitly recorded; no executed gate or test was retried.

Disk checks before heavy preparation and browser release exceeded 50 GB available (310 GB dev-01, 335 GB dev-02 at release). All own test/build/diagnostic jobs have finished. Own targets, dist copies, temporary test Homes and trace/results directories were cleaned on both VMs. Small logs/JSON remain outside the repo; review artifacts are in PR comments. No settings or source PR branches were changed; dev was never merged into or modified.

**Final verdict: NOT GREEN. Never merged.** Train BM8: https://git.kayg.org/kayg/calternal/pulls/862 into `dev`, branch `train/2026-11-02-bm8`, head `4d02e704a281dda00655f45aca6d0d6744fb6357`, assembled on origin/dev `55f35fcde3bc91ca3781055577b93e93819f6cd4`. BM7 #838 is closed with a replacement link. All 16 requested PRs included; **none excluded**. Included source heads captured at assembly time, in ordered --no-ff merges: | PR | Current head at assembly | Merge commit | |---|---|---| | #779 | `7b97e4d14231be07f6a80cb9ca172d02442c487e` | `f8931e0d3081da14f79b5639e33d16c5591b565b` | | #858 | `9c05d871a1692df46eb9e06e0e386bdf8c0bbd9c` | `ac1a23dde8ab32f7033d5b58194b7d25caa99a57` | | #824 | `f1c1aa961e169ef99e132063a97ccbd9a89a5def` | `6e5e659044873b52d95cdca6ee6d4df765ef6a0c` | | #827 | `91a19dcf0a9cf3844861e0e6acf4318618f3ea33` | `c3a626df5418c3b5a1d4e73e4965b0bede7ab925` | | #829 | `d455842942731aabc05474c7929597d57090a7cc` | `807d3b6955df8b6e972fe897f9240cac2e0c4c85` | | #835 | `19071be8455d718822ba98f6b9a402c5fd345242` | `f8268ea5f1be46a2aef2ce04efa83c6c0adcd0fe` | | #828 | `5d7b9b36cd008de78851809724c90a85d92b40a7` | `d3f297b719c4ab851aee749d3a0e3f3737e3624a` | | #833 | `ccaece3dfecbe2048634fc792d7d4a2c6e31f324` | `b2a141ea1553ddd8c8a1de197fb3bd2aa686f60e` | | #836 | `5f7d1a59776411e7f3353d0023817b43affae51e` | `7959619d8ecf3c885d877dc3557cd52fd8eefbb6` | | #831 | `316b43e9578c3e4d2944df8df43caebc611356f7` | `110abecb403bfc6b9e8fd9654a46892c55f62931` | | #844 | `50cce9dcceb87f23a9f346300b3336e315ff6c0c` | `9abec9665d4009563edf380e4ae688c1ea68857e` | | #847 | `40448327a15730271d56226b898e48bcc87b2a4d` | `49ff52c50f6bd9e750c416924783eec7e95a1932` | | #855 | `c38c3083d51f4adcf5cd76a70f1f6960b427413e` | `00b4107c2d713204d8b09ac5f4ed54ec77077aad` | | #857 | `144e717145e35f70356f593b7c04a527e00f7b17` | `02e53ae075e78e4b49be7881459a7ab59f4097ce` | | #856 | `85845e3d11bea351608bde7481779ba2303372e5` | `09bd556a905eac10bb1e4232655220f1b1c2429f` | | #840 | `b68ace95fae5fb05a17697a667f69b020630d136` | `0cbcc5411190f48a955b0bd36aa7cd4aaeacf954` | Mechanical resolutions only: Cargo.lock retains both IMAP UID and markdown-view packages in name order; execute.rs retains the app-password capability/scope guard and starts runtime timing immediately after it. #827 includes 91a19dcf; #828 includes fe46caf5. The final cleanup commit moves #831 measurement artifacts into [a PR comment](https://git.kayg.org/kayg/calternal/pulls/862#issuecomment-74947); no implementation fixes were made during train validation. First-run gates, all with `CI=1`, Nix shell including strace, heavy steps through `~/bin/slot`: | Exact command | Result | |---|---| | `cargo fmt --all --check` | PASS | | `cargo clippy --workspace --all-targets --locked -j4 -- -D warnings` | PASS | | `cargo test --workspace -j4` | PASS, complete workspace including doc tests; deterministic long-note crash test ran and passed | | `cargo build -j4` | PASS on both VMs | | `bun run build` (apps/web) | PASS on both VMs | | `PERF_DIST="$PWD/apps/web/dist" PERF_OUT="$HOME/bm8-train/bundle" PERF_WORK="$HOME/bm8-train/bundle-work" bash perf/web.sh --bundle-only --gate` | FAIL (1): total JS Brotli 324,932 bytes / 317.316 KiB > 315 KiB; origin/dev same command PASS, 319,370 bytes / 311.885 KiB | | `node ci/check-case-paths.mjs` | PASS | | `node ci/check-no-evidence.mjs` | FAIL (1): product source `apps/web/src/composer/ResultsList.svelte` falsely matches `results`; origin/dev lacks this script, so the same command gives module-not-found, not this failure | The bundle excess is 2,372 bytes over budget. First-paint JS (235.316 KiB < 240 KiB) and opt-in plugin JS (37.894 KiB < 45 KiB) pass. The two gate first errors are in [the gate comment](https://git.kayg.org/kayg/calternal/pulls/862#issuecomment-75005), with [one-run baseline results](https://git.kayg.org/kayg/calternal/pulls/862#issuecomment-75007). The evidence-guard failure is a new integration false positive, not an actual captured artifact. Full first Playwright suite, Chromium/dev-01 and WebKit/dev-02 released together at **19:10:19 IST** and **19:10:18 IST**, respectively: | Half | Selected | Passed | Failed | Skipped | Did not run | Exit | |---|---:|---:|---:|---:|---:|---:| | Chromium/dev-01 | 2134 | 1338 | 8 | 781 | 7 | 1 | | WebKit/dev-02 | 2113 | 1312 | 7 | 794 | 0 | 1 | | Total | 4247 | 2650 | 15 | 1575 | 7 | | Both admin-setup cases passed, each in the same invocation as its dependent admin projects. Both JSON reports have zero flaky results, zero global reporter errors and at most one attempt per test. The four Chromium throttled cases were blocked by main-project failures; three sidebar-band-chromium cases were blocked by contrast failures. No blocked case was rerun. Native @perf/SHOTS_DIR and platform skips remain visible in the table. | Host | Project | Passed | Failed | Skipped | Did not run | |---|---|---:|---:|---:|---:| | dev-01 | chromium-1440 | 424 | 2 | 602 | 0 | | dev-01 | chromium-390 | 864 | 3 | 161 | 0 | | dev-01 | throttled-1440 | 0 | 0 | 0 | 2 | | dev-01 | throttled-390 | 0 | 0 | 0 | 2 | | dev-01 | admin-chromium-1440 | 12 | 0 | 9 | 0 | | dev-01 | admin-chromium-390 | 12 | 0 | 9 | 0 | | dev-01 | flow | 1 | 0 | 0 | 0 | | dev-01 | passkeys | 1 | 0 | 0 | 0 | | dev-01 | admin-setup | 1 | 0 | 0 | 0 | | dev-01 | prefs-chromium | 7 | 0 | 0 | 0 | | dev-01 | look-chromium-1440 | 5 | 0 | 0 | 0 | | dev-01 | look-chromium-390 | 5 | 0 | 0 | 0 | | dev-01 | contrast | 6 | 3 | 0 | 0 | | dev-01 | sidebar-band-chromium | 0 | 0 | 0 | 3 | | dev-02 | webkit-1440 | 418 | 3 | 607 | 0 | | dev-02 | webkit-390 | 861 | 4 | 163 | 0 | | dev-02 | admin-webkit-1440 | 9 | 0 | 9 | 0 | | dev-02 | admin-webkit-390 | 9 | 0 | 9 | 0 | | dev-02 | admin-setup | 1 | 0 | 0 | 0 | | dev-02 | prefs-webkit | 3 | 0 | 4 | 0 | | dev-02 | look-webkit-1440 | 4 | 0 | 1 | 0 | | dev-02 | look-webkit-390 | 4 | 0 | 1 | 0 | | dev-02 | sidebar-band-webkit | 3 | 0 | 0 | 0 | Every first error is preserved in the complete catalogs: [all 8 Chromium failures](https://git.kayg.org/kayg/calternal/pulls/862#issuecomment-75168), [all 7 WebKit failures](https://git.kayg.org/kayg/calternal/pulls/862#issuecomment-75176). [Per-failure baseline attribution and all 12 exact baseline commands](https://git.kayg.org/kayg/calternal/pulls/862#issuecomment-75177): **all 15 exact failed train cases passed their matching origin/dev cases**. Fourteen are new/unresolved with no baseline failure signature. The sign-out timeout family also occurred on origin/dev, but in `stuck uplink=false, BroadcastChannel=true`; the train failed the `true,true` variant, which passed on baseline. The family is pre-existing at the spec/signature level; an exact-variant match is unconfirmed. The complete baseline first errors are [posted separately](https://git.kayg.org/kayg/calternal/pulls/862#issuecomment-75173). This does not establish every failure as pre-existing, so the verdict cannot be green. No failure was excused merely because another case in the same file failed. Exact full-browser command in apps/web (WebKit uses `BM8_HALF=webkit`): ```sh export CI=1 BM8_HALF=chromium export CALTERNAL_BIN="$PWD/../../target/debug/calternal" E2E_DIST="$PWD/dist" export TMPDIR="$HOME/bm8-train/browser-tmp" export PLAYWRIGHT_JSON_OUTPUT_NAME="$HOME/bm8-train/playwright.json" bunx playwright test --config=playwright.bm8.config.ts --output="$HOME/bm8-train/test-results" --workers=1 --retries=0 --reporter=line,json ``` The throwaway split config imports the native config, retains all non-WebKit projects on dev-01, and WebKit projects plus admin-setup on dev-02. Only dependencies assigned to the other half are removed; same-half dependencies remain. Its contents were: ```ts import { defineConfig } from '@playwright/test' import base from './playwright.config' const webkit = process.env.BM8_HALF === 'webkit' const projects = (base.projects ?? []).filter(p => p.name === 'admin-setup' || (webkit ? p.name?.includes('webkit') : !p.name?.includes('webkit'))) const names = new Set(projects.map(p => p.name)) export default defineConfig({ ...base, projects: projects.map(p => ({ ...p, dependencies: p.dependencies?.filter(d => names.has(d)) })) }) ``` Preparation: `bun install --frozen-lockfile` at each worktree root. Rust used `CARGO_PROFILE_DEV_DEBUG=0 CARGO_PROFILE_TEST_DEBUG=0 CARGO_INCREMENTAL=0`. Detached jobs used 10800-second unit limits and `~/bin/slot /nix/var/nix/profiles/default/bin/nix develop -c bash <script>`. Dev-02 ran binary/web prerequisites only, then its full browser half. No build or test ran on the control host. The initial Nix working-directory setup error occurred before any gate executed and is [explicitly recorded](https://git.kayg.org/kayg/calternal/pulls/862#issuecomment-74971); no executed gate or test was retried. Disk checks before heavy preparation and browser release exceeded 50 GB available (310 GB dev-01, 335 GB dev-02 at release). All own test/build/diagnostic jobs have finished. Own targets, dist copies, temporary test Homes and trace/results directories were cleaned on both VMs. Small logs/JSON remain outside the repo; review artifacts are in PR comments. No settings or source PR branches were changed; dev was never merged into or modified.
Author
Owner

Superseded by Train BM9: #869 . Closing BM8 as requested; no merge.

Superseded by Train BM9: https://git.kayg.org/kayg/calternal/pulls/869 . Closing BM8 as requested; no merge.
kayg closed this pull request 2026-11-02 16:28:34 +00:00
Some checks failed
CI / server (pull_request) Has been cancelled
CI / rust (pull_request) Has been cancelled
CI / web (pull_request) Has been cancelled
Markdown extraction contract / markdown-native-contract (pull_request) Has been cancelled
Markdown extraction contract / markdown-editor-contract (pull_request) Has been cancelled

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal!862
No description provided.