最終更新: 2026-08-31
運用ルールは .agents/AGENTS.md
参照。本ファイルは未完了 (- [ ])
のみを保持し、 完了した項目は walkthrough.md
へ圧縮記録した上でここから削除する。
src/os_lib/web_engine/css_units.rs +
tests/src/test_css_units.rs(18件) がその第一号。
helpers.rs は super::
の型に依存しホスト側から取り込めないため、 検証したいロジックは
url_resolve.rs / css_units.rs のように
外部依存の無い自己完結モジュールへ切り出してからテストを書くこと。バス幅の不整合: ACMD6 でカードを 4bit
バス幅へ切り替えているのに、 ホスト側 SDHCFG の
WIDE_EXT_BUS(bit2) を立てていなかった。カードが 4 本の DAT
線で送出するデータをホストが 1 本しか読まない状態であり、
コマンド応答は正常なのにデータ転送だけ成立しない症状と整合する。 従来値
0x12 は WIDE_INT_BUS|DATA_IRPT_EN
であり、コード内コメントの 「bit4 = Slow Card / No
DMA」も誤り(SLOW_CARD は bit3、bit4 はデータ
割り込み許可)。ポーリング方式で割り込みハンドラを持たないため、
割り込み許可は落として WIDE_INT_BUS|WIDE_EXT_BUS
に修正した。
ブロック数レジスタ SDHBLC(0x50)
の定義が存在しなかった。 BCM2835 SDHOST では 1
ブロックのバイト数(SDHBCT 0x3C)と転送ブロック数
(SDHBLC 0x50)は別レジスタだが、従来は SDHBCT
に 512 | (1 << 16)
と両方を詰め込んで書いていた。SDHBLC が 0 のままでは
コントローラが転送すべきブロック数を知り得ず、データ転送が開始されない。
SDHSTS のビット定義も誤り。正しくは
bit0 FIFO_ERROR / bit1 CRC7 / bit2 CRC16 / bit3 CMD_TIME_OUT / bit4 REW_TIME_OUT / bit5 SDIO_IRPT / bit8 DATA_FLAG / bit9 BLOCK_IRPT / bit10 BUSY_IRPT
だが、従来は FIFO_DATA=0x200(実際は BLOCK_IRPT)、
FIFO_ERROR=0x008(実際は
CMD_TIME_OUT)と定義していた。とくに 「FIFO
にデータがある」の判定に 0x200 を使っていたため、読み出し
ループがデータ到着を検知できずタイムアウトしていた。0x100(DATA_FLAG)へ修正。
SDEDM(0x34) の FIFO
しきい値を一度も設定していなかったので、 Linux と同じく
read/write とも 4 を設定するようにした。
【現状・要一次情報】 上記 (a)〜(d)
を入れてもデータ転送は依然として
成立しない(sdhost::init()
は成功=ACMD41/CMD2/CMD3/CMD9/CMD7 まで 通るのに、CMD17
の読み出しだけが失敗する)。 重要な前提として、BCM2835 の SDHOST
は公式データシート (BCM2835 ARM
Peripherals)に記載が無い非公開ハードウェアであり、
レジスタ仕様は Linux ドライバ等のリバースエンジニアリング成果しか
情報源が無い。ここまでの修正もその知識に基づくもので、裏取りができて
いない。またテスト環境は QEMU なので、QEMU の
hw/sd/bcm2835_sdhost.c
モデルが何を要求するかが実際の正解を決める。
次に進めるには以下いずれかの一次情報が必要:
hw/sd/bcm2835_sdhost.c(最優先。テスト環境の挙動そのもの)drivers/mmc/host/bcm2835-sdhost.c
推測でのレジスタ変更はこれ以上costがreturnに見合わないため、ここで停止する。退行防止は済んでいる: SDCard::new()
の疎通確認により、データ経路が 使えない場合は従来どおり RAMDisk
へフォールバックする(現在 FS エラー 0 行、 ホスト側テスト 357 件
PASS、cargo build/clippy とも問題なし)。
これが直ると得られるもの: SylFS が実際に永続化し、外部
CSS/フォントの
ディスクキャッシュが起動をまたいで効くようになる。ネットワークが不調でも
一度取得できたリソースから完全再現できるため、本課題の本丸に効く。
ただし 22KB のファイルサイズ上限(FileMetadata を 1
セクタに収める設計で filename に 256B、label に 64B
を固定確保しているため直接ポインタが 44 本
しか置けない)も併せて撤廃しないと modern-style.css(49KB)
や FontAwesome(74KB) はキャッシュできない。間接ブロックの導入か
メタデータのチェーン化が必要。
| 層 | モジュール | 分離状況 | 試験件数 |
|---|---|---|---|
| L2 Ethernet/ARP 送受信 | net/arp.rs |
△ グローバル状態と密結合 | 11 |
| L2 ARP キャッシュ表 | net/arp_cache.rs |
✅ 純粋分離済 | 17 |
| L3 IPv4/checksum | net/addr_util.rs |
✅ 純粋分離済 | 11 |
| L3 ICMP | net/icmp.rs |
△ | 8 |
| L4 UDP 送受信 | net/udp.rs |
△ グローバル状態と密結合 | 6 |
| L4 UDP 受信キュー | net/udp_queue.rs |
✅ 純粋分離済 | 15 |
| L4 TCP ソケット表確保 | net/tcp_state.rs |
✅ 純粋分離済 | 6 |
| L4 TCP 解析境界 | net/tcp.rs |
✅ 異常系網羅 | 13 |
| L4 TCP 状態機械 | net/tcp_state.rs |
✅ 純粋分離済 | 31 |
| L7 HTTP | net/http.rs |
✅ | 13 |
| 実サイト再現(カスケード) | os_lib/css/* |
✅ 実データ検証 | 16 |
今回実施した分離: TCP 受信側状態機械を
src/kernel/net/tcp_state.rs として
TCP_SOCKETS/NET_LOCK/ログから完全に切り離した
(RecvState+Segment→SegmentOutcome
の純粋関数 on_segment())。 tcp.rs 側は
rx_buffer 追記と ACK 送信という副作用のみを担当する。 これにより QEMU
では再現性が無く捕捉できなかったパケットロス・順序
逆転・重複再送・シーケンス番号折り返しを決定論的に検証できるように
なった(tests/src/test_tcp_state.rs、31 件)。
【2026-07-26 追加実施】L2 ARP キャッシュ表を
net/arp_cache.rs へ純粋 分離し、17
件の単体試験を新設した(満杯時の LRU 退避、最近使ったエントリが
退避されないこと、100 回の連続退避で表が壊れないこと、0.0.0.0/ブロード
キャストを保持しないこと、サイズ 0
の表でパニックしないこと、否定キャッシュ の TTL 内スキップ・期限切れ・他
IP への非波及・成功時解除・u64 上限付近
での飽和加算)。あわせて TCP
ソケット表の空きスロット確保も
tcp_state::find_free_slot() として純粋化し 6
件を追加した(従来は 32
スレッドを同時起動する結合テストでしか確認できず不安定だった「満杯なら
tcp socket table full」という性質が、決定論的に検証できるようになった)。
【2026-07-26 追加実施】L4 UDP 受信キューも
net/udp_queue.rs へ純粋分離し、 15
件の単体試験を新設した(満杯時の LRU
退避、空きスロット優先、200 回の
連続退避で表が壊れないこと、ペイロードの上限ちょうど/上限超過時の切り詰め
(破棄しない)、空ペイロード、サイズ 0
キューでパニックしないこと、宛先ポート
による振り分け、述語による照合、最古優先の取り出し、消費済みスロットを再び
拾わないこと、滞留した未消費エントリが新しい応答の格納を妨げないこと
——最後の 1 件は実機ログで 53000〜53003 宛の DNS 応答が 7〜8
件滞留していた 実挙動の裏付け)。
残作業(層別): L3 ICMP と、L2/L4
の送受信本体(arp.rs のフレーム 組み立て・ARP
解決ループ、udp.rs のパケット組み立て)はグローバル状態・
NIC と密結合のままで、既存テストは公開 API
経由の間接的な検証にとどまる。
ただしこれらは「表の操作」ではなく「ハードウェアへの送出」そのものなので、
純粋分離の費用対効果は上記より低い。優先度は下げてよい。 ARP
の解決ループが NET_LOCK
保持中に回っていないかは確認済み・
該当ありで修正した(TCP の ACK 送信経路。詳細は walkthrough.md
参照)。
【2026-08-31
解消確認】「接続回数依存の劣化」は再現しなくなった。 実機ログで
1 ページ読み込みあたり 11〜13 接続すべて成功
([NET][STATS] after-resp total=13 ok=13 dns_fail=0 connect_fail=0 handshake_fail=0)を、本セッション中の複数回の実行で一貫して確認した。
当時の徹底的な仮説排除(NIC送信ドロップ0件・USB転送失敗0件・
USB異常停止0件等)と、いつ・何が効いて直ったかの推定は
walkthrough.md(2026-08-31)へ圧縮記録した。
-netdev user -device usb-netを完全に外した最小構成でも同じ症状
(“Entering main loop”到達後、ログイン用の”testpass”入力すら60秒以内に
処理されないことがある)、(c) ホストのCPU使用率は75%アイドルで
リソース枯渇ではない、(d) ディスクI/O・QEMU起動自体・サーマル状態は
いずれも正常、(e)
bcm2835_gpio_read: Bad offset 18という大量の警告
(1400万行超)がQEMU側-d guest_errorsに出ているが、これは過去の
成功時(sugifinal9等)にも同様の件数で出ており、今回の不安定さの
原因ではない(無害な既存の背景ノイズ)、(f) ゲスト自身のtickカウンタ
(crate::kernel::timer由来、1tick=10ms)でも、ある実行では
“Entering main loop”直後(tick=2)から次のGUI再描画(tick=51914)まで
約519秒が実際に計測されており、ツール側の見かけ上の遅延ではなく
ゲスト内部時計でも同程度の時間が本当に経過していることを確認した。
src/main.rsのメインループ(UART/KBD/MOUSE
FIFO処理→yield_now())
を読んだ限り、人為的なsleepや明らかな遅延要因は見当たらない。
次回引き継ぐべきこと: (1) 実機(Raspberry
Pi実ハードウェア)で 同じ現象が起きるか確認しQEMU固有かを切り分ける、(2)
QEMU自体の
-icountや-d exec,cpu等より詳細なデバッグオプションで実際にCPUが
何を実行し続けているのか(本当にビジーループなのか、それとも
wfe/wfiで待機しているのに起床が遅れているのか)を特定する、make runでの直接目視確認を優先する。 【2026-07-26
追加検証】: (g) os.run("browser", ...)ブラウザ
コマンドを一切送らず、ログイン成功後にただ待ってscreendumpを試すだけの
最小テストでも同じ無応答が発生することを確認した→ブラウザ/ネットワーク
コードとは無関係、問題はログイン成功(アンロック)の前後で発生する
何らかの処理(または単なるタイミング)にあると判明。(h) 逆に別の実行では
ログイン自体(“testpass”の入力処理)すら通らないこともあり、発生箇所が
実行ごとに毎回異なる(アンロック直後のこともあれば、ログイン中の
こともあれば、ネットワーク取得中のこともある)→特定の処理を狙い撃ちした
バグではなく、実行全体にわたる無作為なタイミング不安定性であることが
より確実になった。(i)
このQEMUビルドはhvfアクセラレータを申告上
サポートしている(-accel helpで確認)が、実際に-accel hvfで起動
するとHV_BAD_ARGUMENTで即座に失敗した。ホストはApple
Silicon
(aarch64)でゲストもaarch64のため本来HVFが使えるはずだが使えない
ことから、このコーディング環境自体がネスト仮想化不可のサンドボックス
(クラウドVM)上で動いている可能性が高いと推測される。TCGソフト
ウェアエミュレーションを、それ自体仮想化されたホストの上でネスト実行
すると、外側のハイパーバイザーによるスケジューリングの揺らぎがTCGの
内部タイミング前提を崩し、ゲスト側からもホスト側のtop/uptimeから
も原因が見えない形で不規則な実行停止が起きる、という説明が今のところ
最も筋が通っている。(g)(h)(i)を踏まえ、次回は上記(1)〜(4)に加えて【2026-08-31 現在の環境では未再現】 当時の調査環境は
macOS (Apple Silicon、ネスト仮想化不可のクラウドVM疑い)だった。現在の
開発環境は Windows
11(-accel tcg,thread=multi)で、本セッション中 だけで
make screenshot を十数回実行し、いずれも数分以内に完走・
完全描画を確認しており、519秒級のフリーズやログイン処理の無応答は
一度も再現していない。推定された根本原因(インフラ側のネスト仮想化
スケジューリングの揺らぎ)はコード側で直せるものではなく、環境依存の
問題であるため未解決のまま残す(macOS/クラウドVM環境へ戻った際に
再発しうる)。
net/tcp.rs・icmp.rs・arp.rs・dhcp.rs
の let _ = transmit_...、単体試験 3 件)、format_device()
分離、mount() の Result 化と
main.rs
での判定)と読み出しの「無い」「読めない」の区別
(read_file_checked() 新設・config.rs
を移行)、apps/drive・apps/onedrive
の save_auth 系 呼び出し側)、localStorage
永続化、 Aura の
gh.login/gh.poll/mv。単体試験 27
件。 詳細は walkthrough.md 参照。
残っている主な領域(純粋ロジックはほぼ解消済み。以下はハードウェア
依存か、ホスト試験の土台が無い箇所):
kernel/usb.rs(24)・kernel/tls.rs(9)・kernel/uart.rs(9)・
kernel/sdhost.rs(5)・kernel/draw.rs(6)・os_lib/h264_decode.rs(6。
parse_sps(..).ok() で SPS/PPS の解析失敗が
None になり、以降 「SPS が無い」として静かに復号されない)。
【2026-08-31 完了】web_engine/render.rs: 5
件中 2 件 (el_start
未使用変数抑制・dispatch_key の (bool,bool)
戻り値)は エラー握り潰しではないと確認、残る 3
件(onclick/onreset ハンドラの
js::eval 結果破棄)を layout.rs
と同じログ出力パターンへ修正し 解消済み(詳細は
walkthrough.md 参照)。 ※
js/builtins/dom.rs(12) 等に残る let _ = は
dispatch_event_in_interp((bool,bool)
を返す)や未使用束縛の破棄で
あり、エラーの握り潰しではない(調査済み)。
「別作業者が編集中のため着手しないこと」という当時の制約は、本セッション
でこのファイル群を継続編集してきた実績から陳腐化しており撤回する。
加えて SylFS::read_file の残り約 50
か所の呼び出し側を read_file_checked()
へ移行する作業が残っている(現在は互換ラッパーが
エラーをログへ出すだけで None
を返すため、config.rs 以外の呼び出し側は
依然として「読めない」を「無い」として扱う)。 進め方の原則: (1)
失敗を注入できる形(ホストテストのスタブ or 引数境界)を
先に用意して単体試験を書く、(2) それから実装を直す、(3) caller 側も
戻り値を確認する所まで含めて 1 領域ずつ完了させる。www.sugi-lab.net)
完全再現に向けた既知ギャップ//ajax.googleapis.com/... を自ホスト上の
パスとして取りに行き、返った HTML を JS として実行していた
(unexpected '<' (token #0))。同型の誤りの 4
例目だったので、 判定を url_resolve::split_target
へ集約して打ち止めにした。for-of/for-in
かを試す解析が
失敗するとエラーを記録したまま位置だけ戻すため、for(;;) が
構文エラーになっていた(jQuery は for(;;) を使う)。?.
の直後が数字のときの例外が未実装。ECMAScript
は「?. の次が
数字ならオプショナルチェーンではない」と定める。jQuery の
...test(...)?.01*parseFloat(RegExp.$1)+"":b?... で三項の
? が 食われ、後続の : が孤立していた。
結果、構文エラー 0 件で jQuery 1.8.2
が解析されるようになった。 【2026-09-05 解決】実行時の欠落 6
件も特定・修正し、 $.getJSON が JSON
を取得するところまで動作するようになった (実測:
status=200 本文=150481バイト、JS エラー 0・パニック 0、
スクリプト 3 本すべて完走)。String.prototype
が存在しない。文字列メソッドは実体からの
動的解決だけで賄っており、プロトタイプから直接読む形が undefined。
jQuery は o=String.prototype.trim を読む。Array.prototype
が空(Symbol.unscopables のみ)。同上で
j=Array.prototype.push,k=Array.prototype.slice が
undefined。getElementsByTagName
が切り離しノードで空を返す。文書全体の
検索(query_all)を流用しており、そちらは未接続ノードを除外する。
仕様では接続の有無に関係なく自分の子孫を返す。window.navigator
等がグローバルから写されていない。 window
は個別に用意した props しか持たず、navigator
は別変数だった。window.x = ...
がグローバル変数として見えない。 jQuery は最後に
a.jQuery = a.$ = p で自分を公開するため、 jQuery
が完走しても $ is not defined になっていた。Object.prototype.toString がプリミティブを全部
[object Object] と返す。仕様では
[object String] 等。jQuery は
class2type[toString.call(x)]
で型を判定し、extend(true, ...) の
深いマージで「素のオブジェクトなら再帰マージ」するため、 文字列
"GET" が {} に置き換わり
s.type.toUpperCase() で落ちていた。
型判定の誤りなので影響は jQuery に限らない。
切り分けで得た教訓:
単体試験は空のランタイムで走るため、
is_attached が根(索引
0)まで遡る実装だと最初に作った要素自身が 索引 0
になり偶然「接続済み」になる。この落とし穴で 6 の再現に 5
回失敗した。DOM の接続状態に依存する挙動は実ページで確かめること。
表の動的生成も確認済み: ノード数 187 → 978、要素数 166
→ 1267 へ増加。 取得した JSON から jQuery が DOM を組み立てている。
この項目は解決(未完了の印は、下の残課題のために残す)。
残課題: $.getJSON は
XMLHttpRequest の同期実装を通るため、
取得中はページ全体が止まる。実サイトの JSON は 150KB あり、
体感できる停止になる。非同期化は別項目として扱う。line_break.rs を新設して修正済み(計測・描画の両方)。
残り: “Papers” は依然として 1 文字ずつ縦に潰れる。
切り分けで分かったこと:
上記の折り返し修正を入れた前後で
スクリーンショットがバイト単位で完全に同一だった。
よって web_engine/layout.rs
の文字折り返しループ(current_line を
組み立てる箇所。唯一の折り返し実装)はこの描画経路ではない。
ロゴも 1 行のままで、実物のように 2 行へ折り返していない。 【2026-09-05
原因特定・大部分を修正】診断で要素の矩形を出して特定した。 真因は
flex の基準サイズ(flex base
size)がコンテナ幅だったこと。 全項目の基準が同じ値(実測
base=924)になるため縮小が均等割りになり、 8 項目が一律
88px に揃って描画幅を超え、溢れた項目が描画時に
窓の端で縦に折り返されていた(=“Papers” の 1 文字ずつの縦積み)。
詳細と不変条件は spec/flex_min_size.md(FB-1〜FB-4)。 修正
3 件(基準サイズ・変更後の再レイアウト・横並びの内在幅の総和)で
ナビ 8 項目すべてが正しい幅で表示されるようになった。
残り: ロゴの文字が箱から 20〜40px はみ出して “Top”
と重なる。 実サイトはロゴが 2 行に折り返して収まる。 次は
block_intrinsic_widths
がロゴに対して返す値を実測すること。image_results_ready
のロック(優先度逆転)——直したが再発した。pump() の
lock()(同上)——try_lock にしたが再発した。
ただしどちらも規則(待機・再描画の判定はロックを取らない)に
沿った改善なので、修正自体は残している。[IDLE]
行へ種別ごとの稼働本数(fw/iw)を出すように
した。全体の枠(上限 8)に余裕があっても種別の上限(各 4)で
止まることがあり、合計だけでは区別できないため。strokeText の輪郭描き(現状は塗りで代用)、Path2D(clip(path, rule)
の形は未対応)、imageSmoothingEnabled
未対応)。spec/kernel_performance.md)いずれも未着手・未実測。 着手前に基準値を測ること(L7 側では 5 仮説のうち 当たったのは 1 つだけだった実績があるため、推測で手を入れない)。
【2026-08-31 追記】撮影スクリプトでの
SCREENSHOT_SCROLL(QEMU monitor の sendkey
でPageDownを送りページ下部を検証する機能)が実機で効かず、
ログを調べたところ
[USB] Hub child enumeration completed. 0 HID device(s) ready.
だった。ウィンドウマネージャ側のキー配送経路(add_window の
フォーカス移譲、ProxyApp
経由のキューイング、WebBrowser::on_key)は
静的に読む限り正しく、診断ログでも on_key
自体が一度も呼ばれていない ことを確認した(0 件)。つまり USB
キーボードが列挙されず sendkey
の送り先が無かっただけで、Web エンジン/ウィンドウマネージャの
バグではない。ターミナルへの文字入力は Send-Keys が QEMU の
標準入力(UART 経由)へ直接書いており、USB キーボードを使っていない
ため今まで気づかれなかった。この項目(USB実機確認)が指す不安定さの
具体例として残す。この制約により、2026-07-24
の「JSエンジン実サイト スクリプト対応調査」で残っていた
IntersectionObserver によるカード
類のフェードイン等の視覚効果の目視確認(ページ下部はスクロールしないと
映らない)も未着手のまま。当時のコード監査では
IntersectionObserver.observe()(js/builtins/observers.rsの
io_observe)が observe() 呼び出し時点で
isIntersecting:true
で同期的に即時発火する実装であることを確認済みで、実サイトの
el.style.opacity='0'; observer.observe(el); という順序なら
観測は取れているはず(コード監査のみによる推定、実見未確認)。 USB
キーボード問題が解消されない限り検証できない。
.agents/AGENTS.mdより抜粋)spec/walkthrough.md に記録する
(最大約450〜500行厳守、密に圧縮)。TODO.md
から削除し、常に未完了タスクのみを管理する。tls::FETCH_BYTE_CAP
は未使用の打ち切り機構(2026-08-25 発見)既定 0(無制限)で、設定する呼び出し側が 1 つも無い。 定義と読み出しの 2 箇所しか参照が無く、実質デッドコード。
問題は残し方にある。
判断: 今は誤動作していないので消さずに記録する。 使うことになったら、まず RAII の番人を用意すること。 使う予定が無いと決まったら削除する。