@aiquants/daily-report 0.42.0 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,319 @@
2
2
 
3
3
  All notable changes to `@aiquants/daily-report` are documented here.
4
4
 
5
+ ## Unreleased
6
+
7
+ ### Added
8
+
9
+ - **テストの入口 `@aiquants/daily-report/testing`**: `createDailyReportTestInstrumentation()` を、ホストのテストのビルドがクライアントの入口の
10
+ `DailyReportInstrumentationContext` (既定は null) で画面の周りに配る。計測されたビューだけが試験の口 (`__virtualScroll`) を載せ、行の 2 段目の描画の出来事を数える
11
+ (`rowPass.counters()` の 6 つの数: 同期の描画 `syncReleases`・待たせた見える単位 `deferredVisible`・分け前を超えたフレーム `oversizeFrames`・飛ばした遅れたフレーム
12
+ `lateFrames`・遅れても当てはめたフレーム `lateForcedFrames`・単位のフレーム `unitFrames`。`rowPass.unitKinds()` は単位の種類ごとの最新の p50・p90・予算 (判定の上限:
13
+ 表示のフレームの間隔を速い表示では 60 Hz の間隔で下から押さえ、2 ms を引いた時間))。
14
+ 見積もりの動かしの数 (`estimate.counters()`: `reEstimates` とそのうちビューが動いていたもの `reEstimatesInMotion`) も読める。本番のビルドは何も配らないので、試験の口と数の
15
+ コードはクライアントの束に入らない (`dist/testing.mjs`。gzip で 1,713 B)。型は testing の入口から読む (`DailyReportViewTestHandle`・`DailyReportViewTestReadHandle`・
16
+ `DailyReportRowPassCounters`・`DailyReportRowPassKindReading`・`DailyReportReEstimateCounters`・送り手の出来事 `DailyReportRowPassEvent` と
17
+ `DailyReportRowPassUnitFrameEvent`・`DailyReportRowPassUnitKind`・`DailyReportRowBoxSizing`・`DailyReportRowUnitChange`)。計測を差し込む文脈の型はクライアントの入口
18
+ (`DailyReportInstrumentation`・`DailyReportInstrumentedView`・`DailyReportViewInstrumentation`)。下の Breaking。
19
+ - **行の 2 段目の描画の出来事** (`DailyReportRowPassEvent`): 判定を読むための押し続けの始まり (`hold-start`: そのときのフレームの間隔と、送り手がその keydown の中で聞く
20
+ フレームを求めたか `listening`。測る箱の行を持つビュー (DetailList) だけが真になりうる)・キーのフレームの証拠の標本 (`key-frame`: フレームの時刻・標本・p50・標本の数・
21
+ 間隔・p50 を比べる上限 `limit`)・押し続けの終わり (`hold-end`) が加わり、単位のフレーム (`unit-frame`) はフレームの時刻 (`frameTime`) と当てはめた行の日報 id (`rows`) を、
22
+ 飛ばした遅れたフレーム (`late-frame`) と遅れても当てはめたフレーム (`late-forced-frame`) もフレームの時刻 (`frameTime`。ウィンドウの時計の ms) を持つ。計測は出来事を
23
+ 知らせた時刻でなくフレームの時刻でフレームと照らし、遅れて飛ばしたフレームと単位の無いフレームを見分ける。単位のフレームの予算 (`budget`) は判定の上限。
24
+ - **選択の段階の型 `DailyReportSelectionPhase`** (client エントリ。`"held"` / `"settled"`): ビューの `onSelectItem(id, phase)` の 2 つ目の引数 (下の Changed)。
25
+ - **ID でまとめた詳細の答えの型** `DailyReportSearchDetailsAnswer` (`{ outcomes }`) と `DailyReportSearchDetailOutcome` (共有の入口。下の Breaking)。
26
+
27
+ ### Changed
28
+
29
+ - **押し続けのキーはホストもビューも描き直さない**: 画面の選択のストアは最新の選択 (`get`) と落ち着いた選択 (`getSettled`) を分け、書き込みは段階を必ず持つ
30
+ (`set(id, phase)`)。長押しの流れの中のキー 1 つの選択は `held` で、最新の値だけを変えて誰にも知らせず、ビューはキーの移動の数と選択の見当を流れの終わりまで
31
+ 確定しない。流れの終わりに最後の選択を `settled` で 1 回だけ発行し、ホストとビューは 1 つの `flushSync` で 1 つの揃った状態を確定する (流れの最後のキーが揃えた行は
32
+ 揃え直さない)。ビューの `onSelectItem` は押し続けのキーごとに `(id, "held")`、流れの終わりとほかのどの選択で `(id, "settled")` で呼ばれる (`settled` だけを描くホストは
33
+ 押し続けの間に描き直さない。どの呼び出しも描くホストも、今までどおり動く)。押し続けのキー 1 つの確定は、DetailList も List もホスト 0・ビュー 0・`VirtualScroll` 1・
34
+ 変わった行の枠 2 つで、キーを離した確定はホスト 1・ビュー 1。1 回の押下は長さ 1 の流れで、今までどおり 1 回で確定する。
35
+ - **行の操作部品の Tab 順は登録が書く**: アクティブな行が動いても操作部品の葉 (★・既読・編集・削除・添付の繋がり・コメントの欄) を描き直さず、ビューの登録
36
+ (`createRowControlRegistry`) が前と新しいアクティブな行の操作部品の `tabindex` だけを書く。部品は `useRowControl()` の `{ tabIndex, ref }` を広げ、マウントの時点の
37
+ 値 (サーバーの描画ではスナップショットの値) で描き、後からマウントした操作部品 (本体・編集の形・届いたコメント) も今の状態で入る。描くマークアップは変わらない。
38
+ - **遅い端末の押し続けは、フレームを超える行を名前の付いた殻で入れる** (DetailList): 送り手は装置の定数を持たず、測ったものから判定する。単位の種類の測った p50 が
39
+ 判定の上限 (表示のフレームの間隔から 2 ms を引いた時間。60 Hz より速い表示では間隔を 60 Hz の 16.67 ms として読むので、60・120・144 Hz のどの表示でも 14.67 ms) を超えるか、
40
+ 押し続けの間に DetailList のカードについて、押し続けのキーのフレームの重さの中央値 (3 件から。知らせの busy か、上限を超えて vsync を逃したフレームの拍子の差。送り手が
41
+ 眠った後のフレームの遅れた知らせは数えない) が超えると、その押し続けの間に見える範囲へ入る行は名前の付いた殻のまま入り (送り手は描かずに眠る)、キーを離すと
42
+ アクティブな行のカードから、見えている殻を見える順に描く。名前の付いた殻はカードと同じ名前と、カードの説明のうち描画に依らない部分 (状態の文・件数付きの添付の目印・
43
+ 種別のバッジの文・List のプレビュー・当たったキーワードの数の文) を画面に出さずに持つので、アクティブな行の読み上げは押し続けの間もカードと同じ (当たりを描いた欄の文と
44
+ 表示外の文はカードと一緒に加わる)。表示のフレームの間隔は押し出されなかったフレームの差だけから測るので、4x の装置でも 33.3 ms ではなく 16.67 ms のまま。判定が付く前の
45
+ DetailList の押し続けは、押し続けの始まりの keydown の中で 1 つだけ素で求める聞くフレームから測る (その keydown の中では単位を当てはめず、DOM へ書かず、時計も読まない)。
46
+ 決まった箱の行だけの List は押し続けを聞かない (キーのフレームの証拠が判定するのは測る行だけなので、押し続けの間も走れる単位が無ければ眠る)。
47
+ 1x では 60・120・144 Hz のどの表示でもどの種類も超えないので、どの行も今までどおり入る確定でカードと印まで描く。List の行は 4x でもフレームに収まるので、List は変わらない。
48
+ - 性能の試験 (CPU を押し続けの直前に 4 倍遅くした DetailList の 40 回のキーの押し続け、3 回): 押し続けは 6・7・7 回目のキーで判定し、キーの確定の中で描いた本体は 0
49
+ (0.43.0 は 22・24・25)、名前の付いた殻で入った行は 26・23・26、次のフレームまでの p95 は 45.1・33.4・27.4 ms (中央値 33.4。0.43.0 は 108.6 ms) で、目標の 33 ms
50
+ には届かない (下の Known issues)。読み込みから 4 倍遅い装置も、押し続けの前に描いた行の先の最初の行が入る 16 回目のキーで判定する。
51
+ - **行の高さの見積もりを測った高さから導く** (DetailList): 測っていない行の高さは、ビューが測った行の高さの中央値を 8 px の倍数へ切り上げた見積もり (1 つも測っていない間は
52
+ 440 px) で並べる (0.43.0 は 352 px の決めた値で、測ったどの行より低かった)。見積もりは `@aiquants/virtualscroll` 3.18.0 の `estimatedItemHeight` と
53
+ `setItemHeightEstimate` で渡し、キーの押し続けもスクロールも無いときにだけ当て直す (先頭に見えている日報はその場に留まる)。当て直しは測っていない行だけを動かす:
54
+ VirtualScroll はどの行も `getItemHeight` で読み直すので、測った高さが前の見積もりとちょうど同じ行も、描画の窓の外の測った行も自分の高さを保つ。測り終えるまでの
55
+ DetailList のスクロールバーのつまみの大きさと位置は、見積もりの分だけ 0.43.0 と違う。
56
+ - **速いホイールの間は、ペインの進む先の行から描く**: アクティブな行が描く範囲の外に留め置かれているか無い間は、行の 2 段目の描画が行をペインの進む向きの前から、見える範囲の
57
+ 端からの距離の順に並べ、ペインが動く間は進む先のビューポート 1 つ分の行を、遅れたフレームでも見える範囲へ入る前に描く。React の開発版のビルドで DetailList を 20,000 px
58
+ 送って同じ量で戻す素のホイールは、0.43.0 の 644〜1,116 px から 156〜548 px のずれになった (List は始めの位置へ戻る。下の Known issues)。描いて測った中身は動かない:
59
+ 送る行が描き終えるのを段ごとに待つホイールの往復は、0.43.0 と同じく始めの日報の位置へ戻る。
60
+ - **ID でまとめた詳細は ID ごとの結末で答える** (`reports?ids=`。下の Breaking): 頼んだ ID ごとに頼んだ順の結末 1 つ (`ok` と届ける形の写しの日報・`not_found`・`failed`) で
61
+ 答え、1 件の日報の組み立ての失敗 (表示の欄・写し方・JSON の写し・写しの厳しい読み) はその ID だけの `failed` になって、まとまりのほかの ID は答える (0.43.0 は 1 件の
62
+ 失敗で要求全体が失敗した)。サーバーは `failed` の日報の ID と原因をロガーへ書く (読み取りと日報の組ごとに 60 秒の窓の最初の失敗だけを error で書き、窓の中の後の失敗は
63
+ 数えて、窓の終わりに `failed_report_suppressed count=<数> since=<窓の始まり> read=<読み取り> report=<id>` の warn の 1 行にする)。読み取りは要求のシグナルで止まり、
64
+ 日報ごとの写しを検索の印と同じく 5 ms の区切りごとに日報の境目で譲る (答えのいちばん長い同期の塊は答えの 1 度の直列化)。`not_found` は読めない・無い・削除済みの ID を
65
+ まとめ、区別しない (存在オラクルにしない)。
66
+ 可視性の述語より前か述語にまたがる読み取りの失敗は、今までどおり要求全体の 500。クライアントは `not_found` を null (行の安全弁が一覧から外す)、`failed` をその ID の
67
+ 読み手だけの誤り (`[daily-report] reports answered the outcome failed for report <id>`。新しい読み手が頼み直し、描いている日報は残す) にし、結末の欠けた・重なった・
68
+ 頼んでいない・順の違う答えは答え全体の形の誤りにする。
69
+ - **検索の印は 1 件の壊れた日報でまとまりの印を失わない**: 印の読み取りも ID でまとめた詳細と同じ日報ごとの読み取りを通り、組み立てに失敗した日報は読めない日報と同じく
70
+ 答えに入れずに ID と原因を書く (上と同じ窓で、窓は読み取りごと。0.43.0 は 1 件の失敗で、最大 100 件のまとまりのほかの日報の印もその epoch の間付かなかった)。サーバーは
71
+ 組み立てた最初の日報をいつも答える。
72
+ 届ける形の読みは 5 ms の区切りごとに日報の境目で順を譲り、読み終えたら印の前に 1 回譲る (100 万単位の日報の写しも、印の区切りを 0.43.0 と同じ長さに保つ)。答えの形と
73
+ クライアントは変わらない。
74
+ - **文字は 7:1 (WCAG 1.4.6 AAA)**: パッケージが塗るどの文字の色も、載りうるどの地の上でも、明暗どちらでもどの状態でも 7:1 以上 (文字でない色は 3:1)。控えめな文字
75
+ (節の見出し・メタ情報・置き文字・空のビューの文言・選ばれていないタブ・仮の面・コメントの日時・検索の失敗の行・状態の札ほか) は 1 つの式の色
76
+ (`--aqdr-text-quiet`: 明 `oklch(44% 0.046 257.417)`・暗 `oklch(77% 0.040 256.788)`。slate-500 と slate-400 の彩度と色相で、明度は載る面から求めた値) で、7.06〜9.75:1。
77
+ 色の段を 1 つ濃くしたもの: ID の札 blue-800・区分の札と開発の札と再試行中の札 amber-900・失敗の札 rose-900・添付の名前 blue-800 (暗色 blue-300)・誤りの文言 red-800
78
+ (暗色 red-300)・既定の `Internal` のバッジ emerald-900・コメントの書き手の色 red-800・green-900・blue-800・yellow-900・orange-900 (暗色 red-300・blue-300)・暗色の
79
+ 検索の印 yellow-900 (8.38:1)。白の文字を載せる塗り: コメントの送信 blue-800 (ホバー blue-900)・削除の確認 red-800・誤りの知らせ red-800 (どちらの配色も。閉じる字形は白、
80
+ ホバー red-900)・作成のボタン amber-800 (ホバー amber-900)・ストリームの再試行 rose-100 の地に rose-900 (暗色 rose-900 の地に rose-50)。検索の帯の切り替えの塗りの面は
81
+ 明色が白の文字の blue-800 (8.82:1)、暗色が slate-950 の文字の blue-300 (11.12:1。暗色で白の文字の塗りは、文字の 7:1 と塗りとページの 3:1 を同時には満たせない)。
82
+ 0.43.0 の AA の失敗 (自分のコメントの面の上の緑・黄・橙・赤の書き手、自分のコメントの日時、自動の既読の札の 4.43:1、知らせの閉じる字形) もこれで閉じる。
83
+ 変わるのは色のクラスだけで、行の落ち着いた DOM は 0.43.0 と色のクラスの置き換え (と下の殻の面) のほかは同じ。
84
+ - **殻はカードの面を描く**: 殻と名前の付いた殻の面 (`data-daily-report-row-shell`) は、行の箱を埋めたまま中身の無いカードの面 (角の半径・枠線・色・影) を描く
85
+ (0.43.0 は何も塗らない四角の面)。CPU の遅い端末の押し続けで殻のまま見える行でも、アクティブな行の選択の輪とフォーカスのアウトラインはカードと同じく枠と同心の角で
86
+ 描かれ、カードが載っても休んでいる面の色・枠線・影は変わらない。要素は増えず、配る CSS のクラスの集合も変わらない。
87
+ - **区分のバッジの略語は、見出しが名前を略すときだけ** (WCAG 3.1.4): 見出しが設定の `name` と同じバッジは `<abbr>` を描かない (何も略していない)。
88
+ - **ids の行の `streamAnchor` は空の SSE のストリームで原点 `"0-0"`** (下の Fixed): `null` は位置が分からないとき (Redis のクライアントが無いか読み取りの失敗) と、内部ユーザーの
89
+ 無い閲覧者だけ。SSE のカーソル `0-0` は、ストリームがエントリを 1 つも消していなければ (XINFO STREAM の `entries-added` が `length` と等しい) ストリームの最初のエントリから
90
+ catch-up し (`connected` は `id:` を持たない)、消していれば `resync-required` で答え、空のストリームではライブのエントリだけを配る。どのカーソルでも、空のストリームでは
91
+ catch-up のページを読まず、catch-up の間に届いたエントリで resync しない。
92
+ - **区分のバッジの色はホストが 7:1 に保つ** (README の External Source Badge Configuration): パッケージの 7:1 の守りはパッケージ自身のクラスしか読まないので、ホストの
93
+ `badgeClassName` は文字をバッジの塗り (カードとオーバーレイに重ねたもの) に対して明暗どちらでも 7:1 に保つ。README と設計仕様書の例はその組 (emerald-900 と
94
+ emerald-400 (パッケージの既定)・red-900 と red-300・blue-800 と blue-300) に替わった (前の例の green-800・red-800 は明で、red-400・blue-400 は暗で 7:1 に届かない)。
95
+ - **断られたコメントの投稿は下書きを残す** (WCAG 2.2.5 のパッケージの分): 送る間は欄を読み取り専用にして下書きを残し、受け入れられたら空にする。断られた投稿 (401・403・5xx)
96
+ は打った文字とキャレットをそのまま残す (0.43.0 は前後の空白を除いた文を戻し、キャレットを末尾へ動かした)。
97
+ - 性能の試験 (9 つの条件、30 ms の繰り返しで 40 回のキーの押し続け、各 3 回): どの実行も 40 行を動かし、キーの処理の時間の p95 は最大 4.63 ms、keydown の中の強制したスタイルと
98
+ 配置・届く範囲の内の keydown の中で描いた本体・キーの仕事と同じフレームで確定した単位はどれも 0。合成器の活性化から差し出しまでの待ちはどの条件もキーごとの中央値
99
+ 0.20 ms 以下で 4 ms を超えたキーは 2 つ以下 (List の 80,000 行は、決まった箱の行のビューが押し続けを聞かなくなった後の 3 回で 0.125・0.169・0.135 ms と 0・1・0)。
100
+ 表示の遅れの p95 は DetailList の行 110 から 20.67 ms、16,000 行から答えを
101
+ 見せたまま 21.07 ms (0.43.0 は 21.95 ms)、次のフレームまでの p95 は 17.8〜19.7 ms。80,000 行から答えを見せた押し続けの割り当ては、計測器とカードの窓の中の描画を除いた 0.42.0 と同じ読みで
102
+ 8,518 KiB から 6,849 KiB (19.6 % 減)。
103
+ - **開発者向け**:
104
+ - バンドル: `client.mjs` は gzip で 102,470 B (0.43.0 は 98,384 B) で、3 % の予算 (101,336 B) を超える増えはこの版の実行時の機能 (行の 2 段目の描画の判定と聞くフレーム・
105
+ 行の操作部品の登録・測った見積もり・名前の付いた殻・ID ごとの結末の読み取り) で、増加の台帳 (`bundle-growth-ledger.json`) がモジュールごとの理由を持つ。テストの計測は
106
+ `dist/testing.mjs` (1,713 B) へ出て、クライアントの束から 0.43.0 の試験の口のコードが抜けた。`index.mjs` は 5,522 B で変わらず、`server.mjs` は 53,149 B
107
+ (0.43.0 は 52,158 B。3 % の予算の内)。
108
+ - 仕様書の引用のガード (`spec-citations.guard.spec.ts`) は `src` のどのソースのコメント (JSDoc と行のコメント) の確かめる形の識別子 (`use…`・`create…`・`read…` ほか) も
109
+ `src` の名前と照らす (ほかのパッケージの指定子を所有でつないだ名前は除く。名前は型の位置の文字列のリテラル型も数える)。消した関数の名前を docstring が持ち続けると落ちる。
110
+ - 押し続けの下の行の 2 段目の描画の偽の時計の spec (`rows/hold-schedule.spec.ts`) は、仕事の後の最初の vsync に来るフレームと押し続けの前に変わる CPU の倍率の模型になり、
111
+ 1x と 1.5x の押し続けを判定の無い送り手で記録した痕跡と比べ、アプリで記録した 4x と 1x の押し続けを主スレッドのとおりに再生する (`rows/__fixtures__/`)。
112
+ - 文字の 7:1 の守り `ui/aaa-text-contrast.spec.ts` は、配布するクライアントのどの色の字面も登録した地の上で測り、登録の無い字面を落とす。
113
+ - `VirtualScroll` の代役は、本物が宣言しない名前の props を描画で拒む (`TypeError`)。
114
+
115
+ ### Fixed
116
+
117
+ - **上への押し続けが、眠っていた送り手のまま本体をキーの確定の中で描いた**: 送り手は進む向きを自分のフレームでしか読まなかったので、眠った送り手で始まった上への押し続けは
118
+ 向きを下のまま持ち、上にマウントした行を後ろの行として待たせ、見えるようになったキーの確定の中で描いた (1x の模型で 1 回の押し続けに 4〜5 回)。進む向きは押し続けの
119
+ キーの段から決める。
120
+ - **速いホイールの往復** (0.43.0 の既知の問題): List は始めの位置へ戻る。DetailList は小さくなったが残る (上の Changed と下の Known issues)。
121
+ - **空の SSE のストリームで ids を読んだ後、SSE がつながるまでに公開した日報が届かなかった**: 空のストリームの ids のアンカーは null (位置不明) で、SSE はカーソル無しで
122
+ つながって接続の時点の最新のエントリから配ったので、ids の読み取りと接続の間に公開した日報を配り済みと取り違えた (Redis の再起動・ストリームの刈り込み・新しいワイヤの版の
123
+ 後に起きる)。空のストリームのアンカーはストリームの原点 `"0-0"` で、その後に公開した日報は原点からの catch-up で届く (上の Changed)。
124
+
125
+ ### Breaking
126
+
127
+ - **E2E の試験の口は testing の入口へ移り、テストのビルドでだけ載る**: `DailyReportViewTestHandle`・`DailyReportViewTestReadHandle` は client エントリから
128
+ `@aiquants/daily-report/testing` へ移った。ビューの `__virtualScroll` は、ホストが `DailyReportInstrumentationContext` で計測を配ったときだけ載る (0.43.0 はいつも載せた)。
129
+ Migration: E2E のビルド (たとえば `react-router build --mode test`) でだけ、画面の周りへ `createDailyReportTestInstrumentation()` を
130
+ `<DailyReportInstrumentationContext value={…}>` で配り、型は `@aiquants/daily-report/testing` から読む。本番のビルドは何も配らない。
131
+ - **`reports?ids=` の答えは `{ outcomes }`**: 頼んだ ID ごとの結末の並び (`DailyReportSearchDetailsAnswer`)。サービスの `getDailyReportDetailsByIdsByExternalId` も
132
+ `DailyReportSearchDetailOutcome` の並びを返し、4 つ目の引数に要求のシグナル (`{ signal }`) を必ず取る (`markDailyReportSearchHits` と同じ: どの問い合わせよりも前に確かめ、
133
+ `options` か `signal` が無ければ `TypeError`、ほかの値は `RangeError`。中断されれば次の確かめでその `reason` を投げる)。パッケージのクライアントとサーバーは同じ版で揃える
134
+ (前の形の `{ reports }` はクライアントの読み取りが丸ごと拒む)。
135
+ Migration: 経路やサービスを直に読むホストのコード (結合試験など) は、`outcome` が `ok` の結末の `report` を読み、`not_found` を答えに無い日報として扱う。サービスを直に
136
+ 呼ぶコードは要求のシグナルを渡す (`{ signal: request.signal }`。中断しない呼び出しは `{ signal: new AbortController().signal }`)。
137
+ - **ホストの Redis の口 `DailyReportRedisClient` は `xInfoStream(key)` を必ず持つ**: SSE の catch-up は原点のカーソル `0-0` を確かめるのに XINFO STREAM の `length` と
138
+ `entries-added` を読み、`entries-added` を答えるのは Redis 7.0 以上 (それより古い Redis では、その接続を `producer-failed` で閉じる)。
139
+ Migration: node-redis 5 のクライアントは今のまま型を満たす。口を自分で組むホスト (試験の代役など) は `xInfoStream` を足し、存在するストリームの XINFO STREAM を欄の名前で
140
+ 引ける値で返し、無いキーには拒否で答える。Redis は 7.0 以上にする。
141
+ - **ピアの `@aiquants/virtualscroll` は 3.18.0 以上**: DetailList は行の高さの見積もりを `estimatedItemHeight` と `setItemHeightEstimate` (理由 `"re-estimate"`) で渡す。
142
+ Migration: `@aiquants/virtualscroll` を 3.18.0 以上へ上げる。
143
+
144
+ ### Known issues
145
+
146
+ - **遅い端末の押し続けの次のフレーム**: CPU を 4 倍遅くした DetailList の押し続けの次のフレームまでの p95 は 45.1・33.4・27.4 ms (中央値 33.4) で、目標の 33 ms を満たさない。公開前の計測 (確定した 2 回) では 33.5・51.6 ms。
147
+ p95 のフレームはどれも機能自身のもので、スタイルと配置 5.3・3.9・2.9 ms、塗り 13.5・7.3・8.1 ms、残り (主にスクリプト) 25.4・23.1・14.9 ms。キーのフレームの塗りの変化を
148
+ 無くす形 (合成だけで描く選択の印・先に置く端の行・自分の層のつまみの札) は画素を変えるので、次の版で配置の作業と一緒に試作し、画素の違いを測ってから決める。押し続けの
149
+ 途中で 2 倍に遅くなった装置は、押し続けの中で判定しないことがある (模型の 12 の種のうち 2 つ)。そのときの行は 0.43.0 と同じく入る確定でカードまで描く。
150
+ - **80,000 行から答えを見せた押し続けの表示の遅れ**: p95 は 22.62 ms (実行ごとに 22.59・22.62・23.36 ms) で、どの関門も判定しない。最初のキーの層の新しい塗りが決め、
151
+ DetailList の押し続けでは行の層がキーのフレームごとに新しい ID を取る (押し続けあたり 22〜40 回、新しい層のタイルは 43〜61 %)。画素を変えずに層の ID を留める形はまだ無い。
152
+ - **上への押し続けの合成器の遅れ**: 80,000 行から答えを見せて行 110 から 40 回上へ押し続けると、合成器の待ちの中央値は 1.573 ms で、4 ms を超えたキーは 19 (82 の vsync の
153
+ うち定刻 43・1 フレーム遅れ 36・落とした 3)。上への押し続けは上の行の高さの配達が見える範囲より上の行を動かすので vsync ごとに確定し、休みのフレームが無い: 長いキーの
154
+ フレームか単位のフレームの後に 1 フレームを落とすと、合成器は押し続けの終わりまで 1 フレーム遅れたまま (この確定の形は 0.43.0 の作りのまま。行の描画を区切らない今の版で測った)。
155
+ 遅いホストでは、上のアクティブな行を keydown の中で描くこともある (ある実行で keydown の中の本体 1 つと同期の描画 5 つ。ほかの実行では 0)。次の版で、押し続けの間の
156
+ 高さの配達を待たせる形と、フレームの前の仕事 (ストリームの処理・大きさの無い画像・始まる `pulse`) を外す形を測る。
157
+ - **4x の keyup の後の遅れたフレーム**: キーを離した後、アクティブな行のカードを先に、見えている殻を見える順に描くが、どの実行もカードの間に単位の無いフレームが 1 つある
158
+ (カードがフレームを超え、次のフレームが遅れて始まるので、遅れたフレームとして飛ばす)。アクティブな行のカードまで 3・4・4 フレーム、見えているどの殻のカードまで 5・6・6
159
+ フレーム。次の版で、keyup の後の待たせた見える単位は遅れで飛ばさない。
160
+ - **120 回のキーの押し続け** (報告のみ): DetailList の 16,000 行から答えを見せた 120 回の押し続けは、46 回目のキーで落としたフレームから押し続けの終わりまで 1 フレーム
161
+ 遅れたまま (合成器の待ちの中央値 7.77 ms、120 のキーのうち 4 ms を超えたもの 72)。同期の描画は 11 (120 回の押し続けの模型の帯 10〜13 の中)。List の同じ遅れは、
162
+ 決まった箱の行のビューが押し続けを聞かなくなって消えた (80,000 行の 3 回で 4 ms を超えたキーは 0・1・0)。DetailList は判定が付くまで押し続けのフレームを聞くので、
163
+ 次の版で、決めた停止を入れた DetailList の押し続けを 0.43.0 と比べる。
164
+ - **List の答えを見せた行 110 の keydown の配送**: スクリプトが主の keydown の配送の時間 (計測器を除き、実行ごとのエンジンの探りで正規化) は 61.94 ms で、0.43.0 の
165
+ 56.6 ms を上回る (実行ごとに 69.64・61.94・54.45 ms、0.43.0 は 93.58・56.57・54.55 ms。React の聞き手のスレッドの時間は 61.9・54.6・47.9 ms、0.43.0 は 71.0・49.9・
166
+ 48.2 ms)。この版の変更のうち 1 つで押し続けあたり 1.4 ms を超えるものは無く、次の版でキーの道を測る。
167
+ - **DetailList の素の速いホイールの往復**: 20,000 px を待たずに送って同じ量で戻すと、React の開発版のビルドの E2E で 6 回のどれも始めの位置から 156〜548 px ずれる
168
+ (0.43.0 は 644〜1,116 px。List は 3 回とも戻る)。開発版ではカードを描くのに 10〜26 ms かかるので通り過ぎた行が遅れて測られ、ペインの上で見積もりから測った高さへ
169
+ 変わり、VirtualScroll の位置の補正が見えている行をその場に保つ (見えている行は動かない)。描いて測った中身は動かないので、送る行が描き終えるのを段ごとに待つ往復は
170
+ 0.43.0 と同じく始めの日報の位置へ戻る。次の版で、可視の先頭より上の補正を測っていない行へ当てて、画素の往復を中身の往復にする。
171
+
172
+ ## 0.43.0 (2026-10-14)
173
+
174
+ ### Added
175
+
176
+ - **クライアントの入口の区分のトークンの畳み方** `foldDailyReportSourceTypeToken` (共有の入口と同じ関数): ブラウザーへ配る経路のモジュールは
177
+ `@aiquants/daily-report/client` から読む。共有の入口は束を分けないので、そこから読んだ経路のモジュールは共有のモジュールの 2 つ目の写しを経路のチャンクへ
178
+ 持ち込む (経路のチャンクごとに 3,992 B、gzip で 1,691 B)。
179
+ - **ストリームへ書く形の SSE のメッセージのスキーマと型** `dailyReportSseStreamMessageSchema`・`DailyReportSseStreamMessage` (server エントリ): 共有リーダーが読んだ
180
+ エントリの値 (`DailyReportSseEntryMessage.parsed`) はこの形 (下の Breaking)。
181
+ - **E2E の試験の口の行の 2 段目の描画の数** `rowPass.counters()` (`DailyReportViewTestHandle`。両ビューの `__virtualScroll`): ビューのマウントからの送り手の 4 つの数を
182
+ 読んだ時点の値で返す (`syncReleases`: 確定か知らせのマイクロタスクの確定の中で同期的に描いた単位・`oversizeFrames`: 最初の単位だけで分け前を超えたキーの無い
183
+ フレーム・`lateFrames`: 飛ばした遅れたキーの無いフレーム・`lateForcedFrames`: 遅れていても単位を当てはめたキーの無いフレーム)。ホストの性能の計測は押し続けの
184
+ 前と後に読んで差を取り、送り手の定数も規則も写さない。型だけの公開なのでバンドルは増えない。
185
+
186
+ ### Changed
187
+
188
+ - **検索窓の既定の入力例を短くした**: `searchPlaceholder` の既定を `検索` (en: `Search`) にした。320 px の画面の入力欄 (全角 4 字ぶん) でも切れない。
189
+ 名前 (`searchInputLabel`) は `日報を検索` (en: `Search reports`) のまま。
190
+ - **キーを押し続ける間も、行の単位はキーの無いフレームで 1 つの確定として描く** (行の 2 段目の描画): 殻の行の本体・カードの差し替え・検索の印の変化は、
191
+ どれもビューに 1 つの送り手が自分のフレームのコールバックの中で 1 つの確定 (`flushSync`) として当てはめ、そのフレームの間隔がキーの仕事を持たないときだけ
192
+ 当てはめる: 前のコールバックの後にビューのウィンドウへ keydown か keyup が配られず (受動の捕獲段の聞き手が数えるだけで、DOM を読まない)、長押しの流れが
193
+ フレームのコールバックで段を当てはめず、流れが溜めた段を持たないフレーム。送り手が自分のフレームで当てはめる単位は押し続けの外の 1 回の押下でも同じ規則で、
194
+ キーの描くフレームに載ることは無い (単位を確定する場所はほかに 2 つで、見えるようになった行の単位は見えるようにした確定の中で、押し続けの外で見えている行へ
195
+ 届いた入力は届いた確定か知らせた処理の終わりの確定で描く。下の「行は 1 つの入力の元から単位でだけ描く」)。
196
+ 眠った送り手が起きた後の最初のコールバックは測るだけ。0.42.0 の、次の繰り返しの keydown を見込んで `MessageChannel` の枠で当てはめる形はやめた。
197
+ - **単位の仕事はそのフレームで終わる**: DetailList の行の高さの監視は、配達を 1 つの同期の確定で確定するので、本体の高さ・VirtualScroll の位置 (可視の先頭より
198
+ 上の行の補正を含む)・遠い行を飛ばしてよいかの宣言は、本体を配置したフレームの中で決まり、次のキーのフレームで置き直さない。配達の確定が見せた行と
199
+ 箱を変えた本体は配達が終わる前に測って確定し、観測はビューの次のフレームのコールバックで始める (配達の中で観測を始めないので、
200
+ `ResizeObserver loop completed with undelivered notifications` を出さない)。行の高さの配達はビューに 1 つで、スクロール領域が付くところで行の 2 段目の描画と
201
+ 一緒にビューのウィンドウへつなぐので、観測器もフレームの時計もビューが描かれるウィンドウのもの (iframe の中のビューも、その iframe の観測器と時計を使う)。
202
+ - **フレームごとの単位の数は測った費用で決める**: 最初の単位はいつも、次の単位はその種類の測った費用 (最新 32 回の p90。8 回に満たなければ足さない) の和が
203
+ フレームの間隔の半分とフレームの残りに収まる間だけ当てはめる。費用は行の箱 (List の決まった箱・DetailList の測る本体) と単位の変化 (殻の上の本体・箱を
204
+ 変えうる変化・印だけ) の組の 6 つの種類ごとに測るので、安い印の単位がカードの見積もりを下げない。今の費用では DetailList はフレームにカード 1 枚、List は 1〜3 行。
205
+ - **遅れて始まったフレームは飛ばす**: コールバックの前に GC や長い処理が走ったフレームは、最初の単位の見積もりがフレームの残り (間隔から余白 2 ms と遅れを
206
+ 引いた時間) を超えれば当てはめずに次のキーの無いフレームを待つ。見える帯か届く帯の単位が待つ遅れたフレームは続けて 1 つまで。
207
+ - **押し続けの間は進む向きの先の行を先に描く**: 遠い帯の行のうち、進む向きの前のものは近い帯の後に距離の順に描き (押し続けが届く帯へ入れる行は先に本体を
208
+ 持つ)、後ろのものは押し続けが終わってから描く。同じ順位の単位は先に加えたものが先 (下の Fixed)。
209
+ - **行は 1 つの入力の元から単位でだけ描く**: 両ビューの行コンテナは、日報の詳細と検索の印を行ごとの入力の元から殻のマウントの時点で購読し、届いた値を行の単位で
210
+ 描く (届いた答えが行コンテナをその場で描き直さない)。見えている行へ届いた値は、押し続けの外なら届いた確定か知らせた処理の終わりの 1 つの確定で描く。
211
+ - **日報の詳細は日報ごとに 1 つの記録を分け合う**: 詳細の記録と読み込みは、画面の API の基点と詳細の読み方ごとに 1 つの置き場が日報 id ごとに持ち、id の最初の
212
+ 読み手が読み込みを始め、最後の読み手が止める (止めた後に届いた答えは捨てる)。キャッシュの書き込みはその id の読み手にだけ、答え 1 つに 1 回知らせる。
213
+ `useDailyReportDetail(reportHubId, businessDate)` は引数と返す値をそのままに、置き場の記録を既定の優先度の状態で返す。同じ日報を描く行と側面ペインは記録を
214
+ 分け合うので、どちらかが読む間は取り直しを重ねず、同じ読み込みを待った読み手は誤りも 1 回ずつ受ける。分け合うのは決着した日報で、決着した読み込みの誤りは
215
+ 分け合わない: 誤りを持つ記録へ後から加わった読み手 (載った行・入力の先導の帯に入った id・側面ペイン) は頼み直し、答えが誤りを消す。頼み直すのは新しい購読だけ
216
+ (描き直しと同じ聞き手の 2 度目の購読では頼み直さないので、誤りを描き続ける行が頼み直しを繰り返さない) で、閲覧者のセッションの断り (401・403) は日報と同じく
217
+ 分け合って頼み直さない。鮮度も新しい読み手が加わるときに確かめる: キャッシュの項目が期限切れになった記録へ、答えを待っていないときに新しい読み手が加わると
218
+ (日報を選び直して作り直される側面ペイン・載った行・先導の帯に入った id)、行が記録を開いたままでも `DAILY_REPORT_FETCH_DEBOUNCE_MS` の後に取り直す (期限切れの
219
+ 日報を描いたままで、答えまでに加わる読み手は 1 本の要求を分け合う)。描いている日報の取り直しが一時の失敗で終わっても日報を誤りに替えず (0.42.0 は読み手が誤りを
220
+ 描いた)、`[DailyReportDetailLoader]` へ警告を書いて、次の新しい読み手がまた取り直す。セッションの断りは描いている日報の上でも誤りになる。
221
+ - **検索の答えを見せている間、ビューは行の入力を先に読む**: 描いている行と、描いている範囲の両端の先の 24〜40 行の詳細と印を置き場へ購読させておく (進む側が
222
+ 24 行を切ると 8 行ずつ伸ばし、跳んだ後は両側 32 行)。押し続けの間に載る行は詳細と印を置き場に見つけ、最初の本体から印つきのカードを描く。押し続けの間の
223
+ 要求は置き場ごとに 40 回のキーで 4〜5 本、跳んだ後は置き場ごとに 1 本で、描いている窓と 2 × 32 行の 95〜96 件を運ぶ (描いている窓だけの約 31 件から増える)。
224
+ 借りは置き場ごとに持ち、置き場が替わると替わった置き場の購読だけを返す: 新しい答え (印の置き場の替わり) は印だけを借り直し、詳細の借りと送っている詳細の
225
+ 要求はそのまま (同じ ID をもう一度頼まない)。営業日で読む一覧は今までどおり営業日の先読み (`useDailyReportPrefetch`)。
226
+ - **検索の印は行の単位の確定で描く**: 印を知っているカードは最初の確定から印つきで描き (素の文字を塗ってから印を塗り直さない)、届いた印は行の 1 つの単位で描く
227
+ (印だけの変化は行の箱を変えないので、行は飛ばしてよいまま)。一致の説明は、カードを確定した処理のうちに揃う。印の葉は配置の効果 1 つで登録し、集計の登録は
228
+ 札を返して札の登録だけを消す。側面ペインは今までどおり、最初の描画を素の文字で描き、印を後追いの描画で塗る。組み立てた CSS は 0.42.0 と同じで、行の落ち着いた DOM は
229
+ 0.42.0 と画面に出さない文だけが違う (下の 2 つの項: カードの無い行の説明と List のカードの一致の説明)。
230
+ - 性能の試験 (9 つの条件、30 ms の繰り返しで 40 回のキーの押し続け、各 3 回): キーの届く範囲の内の keydown とキーのフレームの中で描いた行の本体は 0 (0.42.0 は
231
+ 実行ごとに keydown の中で 0〜7、キーのフレームで 2〜7)、キーの仕事と同じフレームで確定した単位 0、予算を超えたフレームは、0.42.0 の計測の数え方 (描画の段から次の描画の段まで) で
232
+ DetailList の行 110・16,000 行・80,000 行から (答えを見せたまま) 1 / 4 / 2 → 0 / 0 / 2、今の数え方 (ブラウザーのフレームごとに、vsync か前の確定から自分の
233
+ 確定まで。vsync の前の GC はそのフレームに入らない) で 0.43.0 は 0 / 0 / 1、表示の遅れの p95 は 25.57 / 26.28 ms → 20.52 / 21.95 ms (80,000 行からは 26.35 → 26.68 ms で、
234
+ キーのフレームの層の描き直しが決める)、キーの処理の時間の p95 は最大 3.85 ms、見える範囲へ入った行がカードを待つ時間 (0.42.0 の最大 331.5 ms) は 0。
235
+ CPU を 4 倍遅くした押し続けでは、見える範囲へ入るどの行もその確定でカードと印まで描き、次のフレームまでの p95 は 108.6 ms (0.42.0 は 50.2 ms。キーの
236
+ フレームそのものの描画が決める)。
237
+ - **要求の隔離は `@aiquants/request-isolation` の判定**: 別オリジンの判定 (`crossOriginRefusal`) を共有のパッケージから読む (依存 `@aiquants/request-isolation`
238
+ `^0.1.0` が加わる)。どの要求への答えも変わらない。拒否の記録の行は、判定が拒んだ理由を持つ (`reason=cross-origin-fetch` は Fetch Metadata が別のオリジンを
239
+ 示した要求、`reason=foreign-origin` は Fetch Metadata が無く、`Origin` が宛先のホストを名乗らない安全でないメソッドの要求。下の Breaking)。
240
+ - **自分の作成・更新・公開を、自分のほかのタブと端末は自分の既読とスターのまま受け取る**: 日報を埋め込む SSE の知らせ (`report-create`・`report-update`・
241
+ `report-publish`) は、閲覧者がその書き込みの操作者か (`viewerIsActor`) を接続ごとに持つ。クライアントは真なら知らせの既読とスターを自分の書き込みの答えとして
242
+ 書き (作った日報は既読)、偽なら今までどおりほかの閲覧者の書き込みとして閲覧者ごとの欄を手元の値か偽に合わせる。操作者の内部 ID は閲覧者へ届かない
243
+ (下の Breaking)。
244
+ - **作成の答えは読み直した日報だけ**: `createDailyReport` は確定の後に日報を読み直せなければ、書いた値から組んだ日報で答えず、名前の付いた誤りで止まる
245
+ (下の Breaking)。
246
+ - **カードの無い行は描いている状態で説明する**: 殻とカードの無い行は今までどおり営業日の ISO 表記で名付け、目に見える状態をアクティブな子孫の読み上げへ届ける
247
+ (`aria-describedby`): 読み込みに失敗した行は誤りの段落 (List は `reportSummaryLoadFailed`、DetailList は `reportLoadFailed`)、List のサーバーに無かった行は
248
+ 不在の段落 (`reportNotFound`)、DetailList の編集フォームの行 (表示中ユーザーの下書きが自動で開く編集を含む) は画面に出さない編集の状態 (新しいラベル
249
+ `rowEditingState({ isDraft })`: en `Editing draft` / `Editing`、ja 「下書きを編集中」 / 「編集中」。位置を外れた要素なので行の高さは変わらない)。殻と読み込み中の
250
+ スケルトンは説明を持たない。0.42.0 はどれも日付だけを読ませた。固有キーは 110 から 111 (文字列のキー 89・整形関数 22。キーの表は全 122 キー。下の Breaking)。
251
+ カードと誤りの面はどちらも単位の写しから描くので、単位を待つ間に届いた読み込みの誤りは、単位が描くまで行をスケルトンのまま読み込み中にする (「見つかりません」を
252
+ 挟まない)。
253
+ - **List のカードの一致の説明は DetailList の一致の印と同じ文を言う**: 検索の答えを見せている間の List のカードの隠した一致の説明は、同じ答えの下の同じ日報について
254
+ DetailList の行の一致の印と同じ 1 つの規則で、当たったキーワードの数の文 (2 つ以上のキーワードの OR)・当たりを描いた欄の文・表示外の文を言う (カードの葉が当たりを
255
+ 1 つも描かない日報、たとえばプレビューの外の本文にだけ当たった日報は、表示外の文)。0.42.0 の List は当たりを描いた欄の文だけで、答えの行の多くが何も言わなかった。
256
+ 見える印は持たず、要素は箱を持たないので行の高さは変わらない。
257
+ - **開発者向け**:
258
+ - `publish:*` は verify の後に基準を書き (`--write`)、ビルドし直してから `--exact` と漏洩の検査を走らせる (検査は `--exact` が測ったビルドを読む)。公開の配線の
259
+ 検査は、最後のビルドから漏洩の検査までに読み取り専用の検査だけを認める。
260
+ - 押し続けの下の行の 2 段目の描画は偽の時計の spec (`rows/hold-schedule.spec.ts`。本物の送り手と選択の置き場と長押しの流れ) が、行の高さの配達
261
+ (`rows/row-height-delivery.ts`) とその落ち着かせる範囲は仕様に沿った偽の ResizeObserver (`test-helpers/fake-resize-observer.ts`。偽物の規則そのものも
262
+ `test-helpers/fake-resize-observer.spec.ts` が確かめる) とテストが回すフレームのウィンドウの上の `rows/row-height-delivery.spec.ts` が確かめ、行の落ち着いた DOM は
263
+ 0.42.0 の写し (`components/__fixtures__/settled-rows-0.42.0.json`) に意図した変更 (画面に出さない文だけ) を当てたものと比べる。
264
+ - 送り手のフレームのコールバックの名前 (`rows/row-pass.ts` の `runFrame`。ホストの性能の計測が source map で単位のフレームを数える名前) は
265
+ `rows/row-pass.guard.spec.ts` が守る (名前を変える・移す・包むと、性能の計測の後ではなくパッケージのテストで落ちる)。
266
+
267
+ ### Fixed
268
+
269
+ - **同じ順位の待つ単位は先に加えたものが先**: 待つ単位の順位を位ごとに比べる。0.42.0 は一覧を去った日報の単位 (距離が分からない) どうしを NaN で比べ、後に
270
+ 加えたものを先にした。
271
+
272
+ ### Breaking
273
+
274
+ - **閲覧者へ届く日報の知らせは `viewerIsActor` を必ず持つ**: `DailyReportSseMessage` (共有の入口) の `report-create`・`report-update`・`report-publish` は
275
+ `viewerIsActor: boolean` を持ち、`dailyReportSseMessageSchema` はそれの無い知らせを通さない。
276
+ Migration: 日報の知らせを組み立てて SSE のハンドラーやクライアントへ渡すホストの試験は、`viewerIsActor` を足す (ほかの閲覧者の書き込みなら `false`)。
277
+ - **ストリームのエントリを読んだ値はストリームへ書く形**: `DailyReportSseEntryMessage.parsed` は `DailyReportSseStreamMessage` (日報を埋め込む 3 種別はサーバーだけが
278
+ 読む `actorRawUserId` を必ず持ち、`viewerIsActor` を持たない)。
279
+ Migration: 自分の共有リーダー (`DailyReportSseReaderPort`) を渡すホストは、エントリを `dailyReportSseStreamMessageSchema` で読む (server エントリの
280
+ `readDailyReportSseStreamEntry` がこの読み方をする)。ストリームへ直接書くホスト (結合試験など) は、日報を埋め込む知らせに書き込みの操作者の内部 ID を
281
+ `actorRawUserId` で書く。
282
+ - **SSE のワイヤの版は v4**: ストリームのキーは `<streamKey>:v4` で、0.42.0 が書いた v3 のエントリは読まない (開き直したクライアントは 1 度だけ一覧を合わせ直す)。
283
+ Migration: 同じストリームを読み書きするサーバーは同じ版へ揃えて配備する (版の違うサーバーは別のキーへ書き、互いのエントリを配らない)。v3 のストリームは
284
+ 書かれなくなるので、手で消してよい。
285
+ - **拒否の記録の行は `reason=` を持つ**: `cross_site_rejected route=<経路> reason=<cross-origin-fetch|foreign-origin> site=… mode=… dest=… method=…`。
286
+ Migration: 行を読むログの検索や監視は、`route=` の後の `reason=` を受ける。
287
+ - **作成が読み直せなければ誤りで止まる**: `createDailyReport` は、確定の後の知らせの中身の読み取りが日報を見つけなければ
288
+ `[daily-report] createDailyReport: report <id> was committed but its detail read returned nothing` (何も知らせない)、操作者の範囲の読み直しが日報を返さなければ
289
+ `[daily-report] createDailyReport: the actor's scope cannot read report <id> it just created` (知らせの後) を投げる。どちらも確定の後の競合か欠陥で、作成の関門を
290
+ 通った作成者は自分の下書きを読める。
291
+ Migration: 手で書いた db の代役でサービスの作成を試す試験は、作成の後の 2 つの読み取り (知らせの中身と操作者の範囲) に作った日報を答える。
292
+ - **閲覧者へ届く SSE の知らせは宛先を持たない**: `DailyReportSseMessage` (共有の入口) と `dailyReportSseMessageSchema` のどの種別も `recipientRawUserId` を宣言しない
293
+ (0.42.0 は `status-update`・`comment-add`・`comment-delete` と日報を埋め込む 3 種別が持った)。宛先はストリームへ書く形 (`DailyReportSseStreamMessage`・
294
+ `dailyReportSseStreamMessageSchema`) だけが持ち、`status-update`・`comment-add`・`comment-delete` も宛先を持つ書く形のスキーマで読む。届く形で読んだ値は宛先を落とす。
295
+ Migration: 知らせの宛先を読むホストのコード (自分の宛先の絞り込みなど) は、書く形 (`DailyReportSseStreamMessage`) から読む。ストリームへ直接書くホストは
296
+ 今までどおり宛先を書く (書く形が受ける)。
297
+ - **`DailyReportSseEntryMessage` は `text` を持たない**: 共有リーダーが読んだエントリの値は、書く形のスキーマが検証したメッセージ (`parsed`) と書かれたままの JSON の値
298
+ (`published`。宛先の絞り込みだけが読む) の 2 つ。
299
+ Migration: 自分の共有リーダーを渡すホストは、エントリの値に `text` を作らない (server エントリの `readDailyReportSseStreamEntry` の読み方)。
300
+ - **閲覧者へ書く SSE のフレームは、読んだメッセージから組んだ届く形の鍵だけの 1 行**: どのフレームも、書く形のスキーマが読んだメッセージからサーバーだけが読む欄
301
+ (`recipientRawUserId`・`actorRawUserId`) を除いて、届く形のメッセージとして組んだ JSON。書く形のスキーマが知らない鍵は届かず、書かれた値が整形の改行を持っていても
302
+ フレームは 1 行 (0.42.0 は宛先もコメントの秘匿も要らない知らせを書かれた字面のまま送り、ほかは書かれた値から宛先を除いて送ったので、知らない鍵も届いた)。
303
+ Migration: 知らせに欄を足すホストは、書く形と届く形の両方のスキーマに足す (書く形にだけ足した欄は閲覧者へ届かない)。届く形のスキーマは今までどおり知らない鍵を
304
+ 拒まずに落とすので、長く開いたタブは新しいサーバーが欄を足した知らせも読み続ける。
305
+ - **client の型 `DailyReportLabels` に必須のキー `rowEditingState({ isDraft })` が増えた** (上の Changed の「カードの無い行は描いている状態で説明する」)。
306
+ Migration: `DailyReportLabels` を丸ごと組むホスト (試験の代役など) はこのキーも書く。上書き (`DailyReportLabelOverrides`) は部分のままでよい。
307
+
308
+ ### Known issues
309
+
310
+ - **速いホイールの往復**: 描いている窓のずっと先まで 1 度にホイールで送り (DetailList で 20,000 px など)、同じ量で戻すと、始めの位置から 644〜1,116 px ずれた所に着く
311
+ (0.42.0 は始めの位置へ戻った)。送る間に殻のまま通り過ぎた行が後でペインの上で描かれて、測る前の見積もり (352 px。測ったどの行より低い) から測った高さへ伸び、
312
+ VirtualScroll の位置の補正が見えている行をその場に保つため (見えている行は動かない)。次の版で、2 段目の描画の順をペインの進む向きの距離で決め、測る前の見積もりを
313
+ 測った高さから導く。
314
+ - **遅い端末での押し続け**: CPU を 4 倍遅くすると、キーを押し続ける間の描画は約 9 枚/秒 (次のフレームまでの p95 は 108.6 ms で、目標の 33 ms を満たさない。
315
+ 0.42.0 は 50.2 ms)。どの行も見える範囲へ入るその確定でカードと印まで描き終える。時間を決めるのはキーのフレームそのもののスタイル・配置・塗りで、キー 1 つの
316
+ 仕事を安くするのは次の版の作業。
317
+
5
318
  ## 0.42.0 (2026-10-12)
6
319
 
7
320
  ### Added