Skip to content

v0.0.29

Choose a tag to compare

@github-actions github-actions released this 08 Oct 02:16
· 288 commits to main since this release

0.0.29

  • (EN) Follow oep-spec d801f02 .. c6ab5d9 (capture: the probe's and the clients' questions). Registry and vectors synced;
    README names c6ab5d9. configure sends the §3.3 table's TLVs and trigger_track without the critical bit, only multirate
    critical; rate / samples / segments of 0 and a type 0 trigger's role and value (sent 0) are handled before sending;
    nothing relies on the order of refusals. After an error stop the bytes received at or past status's write_pos are
    dropped (Received drop_from / dropFrom, finish). A capture-group's pretrigger is the trigger_track's alone (bind refuses a
    track with its own before sending; P_k helper; bind's unavailable cause 2 names the fn). A broken multirate declaration
    is not used. Virtual bench: bind computes P_k for every other track and refuses one that cannot keep it (cause 2,
    TLV fn); a streaming error stop moves write_pos back to the start of the segment not kept.
  • (JA) oep-spec d801f02〜c6ab5d9(キャプチャ: probe と client の実装からの質問)に合わせた。 registry と vectors を同期し、
    README は c6ab5d9 を名乗る。configure は §3.3 の表の TLV と trigger_track を critical の bit 無しで送り、critical は multirate だけ。
    rate / samples / segments の 0 は送る前に断り、type 0 のトリガは role と value を 0 で送る。断りの順には頼らない。エラーで止まったら
    status の write_pos 以降に受け取ったバイトを捨てる(Received の drop_from / dropFrom、finish)。capture-group の pretrigger は
    trigger_track のものだけ(自分の pretrigger を持つトラックは送る前に断る。P_k の計算。bind の unavailable cause 2 は fn を名指す)。
    壊れた multirate の宣言は使わない。仮想ベンチ: bind はほかのトラックの P_k を計算し、持てないトラックを断る(cause 2、TLV fn)。
    ストリーミングがエラーで止まると write_pos を、保てなかった区画の先頭に戻す。
  • (EN) Follow oep-spec 66c49e7 .. dd5a886 (logic capture: any w, seamless segments, multirate). Registry and vectors
    synced (logic_layout.json, multirate.json); README names dd5a886.
    • §1.1: w is any integer 1-128 - the client already read bit i*w + pos[k]; the virtual bench packs any w
      (virtual_bench_capture.pack_samples) and ends segments on a whole byte for any w.
    • §2.2: a segment the probe cannot keep seamless is not handed out; the track stops with state 6, stopped reason 3,
      error 2 (1 for DMA / peripheral). The virtual bench models it: Endpoint.capture_overflow(fn, error) and
      virtual_bench_serve's stdin command capture-overflow FN [ERROR]; its status now carries the TLV error in state 6;
      a bound track's group stops the others.
    • §5 multirate: new module multirate (Multirate, Declared, Layout with decode / encode, check, the policy
      enum); LogicCapture.configure(multirate=[Multirate(role, policy, d, param), ...]) sends TLV 0xE0 per role after
      checking describe (multirate_declared(); d 1 sample always accepted, min_d / max_d bound d >= 2, dd5a886),
      Config.block / multirate_layout() / segment_bytes(), read_segment and decode_multirate(data, samples).
      The virtual bench serves it on p4-x035's logic (describe 0x60, §5.2's refusals, block L, rounding, a lower base
      rate for heavy combinations, one-shot / repeat / streaming as blocks, rate_range exact). oep dump shows it.
  • (JA) oep-spec 66c49e7〜dd5a886(ロジックキャプチャ: 任意の w、連続な区画、multirate)に合わせた。 registry と vectors
    (logic_layout.json、multirate.json)を同期し、README は dd5a886 を名乗る。
    • §1.1: w は 1〜128 の任意の整数 ― client はもともとビット i·w + pos[k] を読んでいる。仮想ベンチは任意の w で詰め
      (virtual_bench_capture.pack_samples)、どの w でも区画をバイトの境目で終える。
    • §2.2: 連続に保てない区画は出さず、トラックは state 6、stopped reason 3、error 2(DMA / ペリフェラルなら 1)で止まる。仮想ベンチは
      Endpoint.capture_overflow(fn, error) と virtual_bench_serve の stdin のコマンド capture-overflow FN [ERROR] でこれを再現し、
      state 6 の status に TLV error を付け、束ねたトラックなら組がほかを止める。
    • §5 multirate: 新しいモジュール multirate(Multirate、Declared、decode / encode を持つ Layout、check、方針の enum)。
      LogicCapture.configure(multirate=[Multirate(role, policy, d, param), ...]) は describe を確かめてから(multirate_declared()。
      d 1 の sample はいつも扱い、min_d / max_d は d ≥ 2 だけを縛る、dd5a886)役割ごとに TLV 0xE0 を送る。Config.block /
      multirate_layout() / segment_bytes()、read_segment と decode_multirate(data, samples)。仮想ベンチは p4-x035 のロジックで
      答える(describe 0x60、§5.2 の断り、block L、丸め、重い組み合わせでの低い base rate、ワンショット / リピート / ストリーミングを
      block で、rate_range の exact)。oep dump も表示する。
  • (EN) Follow oep-spec 0098b56 .. 2c6d18d (the external review's interface re-check of 0991759). Registry and
    vectors synced; README names 2c6d18d. Breaking (wire and API):
    • Capture §3.3 contract: LogicCapture.configure always sends mode and rate, requires samples in one-shot and
      repeat, refuses it in streaming, sends segments in repeat only and pretrigger only with a trigger (ValueError
      before sending; a pretrigger of 0 without a trigger is left out). One-shot's answer has no actual_segments
      (Config.segments is 1).
    • Generations (§3.4): 1 after 0xFFFFFFFF (message.next_generation), compared for equality. Every capture event
      carries its generation: capture.unpack_event / Event, LogicCapture.events(link) and CaptureGroup.events(link)
      take a fn's events off the link and drop those of an earlier start (stale_events).
    • capture-group (§4): start's answer is blocking_ms, start_ns, the group's generation, n, n x (fn, generation) - the
      TLV 0x01 and capture.GROUP_GENERATIONS are gone. CaptureGroup.start still returns (blocking_ms, start_ns) and
      fills each track's generation; CaptureGroup.generation / GroupStatus.generation are the group's.
    • Serial paging (common §1.3): marks and capture segments answer from from_serial inclusive, nothing and more 0 from
      the next serial, the oldest kept for a serial not kept; Console.marks / LogicCapture.segments continue from the
      last serial + 1 (mod 2^32) until more 0 (message.serial_page_start in the virtual bench). Console streams takes
      first(u16).
    • Debug: a step that is not status ok raises StepError with dpc_before / dpc_after None (the probe sends 0);
      RunResult.dpc_valid / where() never show an invalid dpc (ch32_flash uses them); arm-adi transfer of n = 0
      returns [].
    • spi-target: fixture.wire_bits / pack_wire_bits (bit k in byte k / 8, MSB or LSB first, a partial last byte's
      missing bits 0); read_rx's ns is when CS went inactive (I2C: the STOP or next START).
    • Virtual bench: the configure contract and answer rows, wrapping generations and serials, generations on every
      event and in the group's start answer / status, marks and segments paging, release by core §2.6, step's zeros,
      spi-target masking and an optional ns (Endpoint.spi_ns). New tests/test_spec_2c6d18d.py; the new vectors and
      ops.json events run both ways (arm-adi's probe side skipped: the virtual bench has no arm-adi).
  • (JA) oep-spec 0098b56〜2c6d18d(0991759 の外部レビューのインターフェース再確認)に合わせた。 registry と vectors を同期し、
    README は 2c6d18d を名乗る。壊れる変更(wire と API):
    • キャプチャ §3.3 の契約: LogicCapture.configure は mode と rate をいつも送り、ワンショットとリピートでは samples が必須、
      ストリーミングでは断り、segments はリピートだけ、pretrigger はトリガがあるときだけ送る(送る前に ValueError。トリガ無しの
      pretrigger 0 は送らない)。ワンショットの応答には actual_segments が無い(Config.segments は 1)。
    • 世代(§3.4): 0xFFFFFFFF の次は 1(message.next_generation)、等しいかだけを比べる。キャプチャの出来事はどれも世代を持つ:
      capture.unpack_event / Event、LogicCapture.events(link) と CaptureGroup.events(link) はその fn の出来事を link から取り、
      前の start のものは捨てる(stale_events)。
    • capture-group(§4): start の応答は blocking_ms、start_ns、組の世代、n、n × (fn, generation)。TLV 0x01 と
      capture.GROUP_GENERATIONS は無くなった。CaptureGroup.start は今までどおり (blocking_ms, start_ns) を返し、各トラックの
      generation を入れる。CaptureGroup.generation / GroupStatus.generation は組の世代。
    • 通し番号のページング(共通部品 §1.3): marks とキャプチャの segments は from_serial を含めて答え、次の番号からは 0 個で
      more 0、残っていない番号は残っている一番古いものから。Console.marks / LogicCapture.segments は最後の serial + 1(2^32 の余り)
      から more 0 まで続ける(仮想ベンチは message.serial_page_start)。console の streams は first(u16)。
    • デバッグ: status ok でない step は dpc_before / dpc_after を None にして StepError(probe は 0 を送る)。RunResult.dpc_valid /
      where() で無効な dpc を見せない(ch32_flash も使う)。arm-adi の n = 0 の transfer は [] を返す。
    • spi-target: fixture.wire_bits / pack_wire_bits(ビット k は byte k / 8、MSB か LSB が先、最後の byte の来なかったビットは 0)。
      read_rx の ns は CS が無効になった時刻(I2C は STOP か次の START)。
    • 仮想ベンチ: configure の契約と応答の行、一周する世代と serial、どの出来事にも世代、組の start の応答と status の世代、marks と
      segments のページング、core §2.6 での release、step の 0、spi-target のビットの消去と任意の ns(Endpoint.spi_ns)。
      tests/test_spec_2c6d18d.py を足した。新しい vectors と ops.json の events を両側から通す(arm-adi の probe 側は飛ばす: 仮想ベンチに
      arm-adi は無い)。
  • (EN) oep find and discovery.find_unit verify what they find (oep-spec 0991759, host guide §4.1). The DNS-SD
    service name oep is not registered, so another service may advertise _oep._tcp. discovery.verify(found) checks
    every instance at once (one thread each, 1 s per answer): a TCP connection, confirm (an OEP! answer, the probing
    rule) and fn 0's describe, whose unit_id must be the TXT unit_id; no session is opened. discovery.check(f) is one
    instance's check (None, or what it said). oep find lists only verified instances and warns on stderr for each
    dropped one, naming its endpoint and what it said (none left: "no verified probe", exit 1); --no-verify lists them
    raw, --verify-timeout S sets the wait.
    find_unit (and so tcp:UNIT_ID and its reopen) takes the first verified instance with that TXT unit_id and passes
    over the others (verified=False: the first announced one); open_host still checks confirm and describe's unit_id on the link it opens (checked: unchanged).
    tests/test_virtual_bench_announce.py adds in-process responders announcing a non-OEP TCP service (under its own
    unit_id and under the bench's) and the bench's port under a wrong TXT unit_id; tests/test_wifi_and_tcp_discovery.py
    follows (its made-up instances use --no-verify / verified=False, a wrong TXT is now dropped by find_unit, and
    open_host's own unit_id check is still tested).
  • (JA) oep find と discovery.find_unit は見つけたものを確かめる(oep-spec 0991759、host ガイド §4.1)。 DNS-SD の
    service の名前 oep は登録した名前ではないので、別のサービスが _oep._tcp を広告しうる。discovery.verify(found) はどの
    instance もすべて同時に(1 つに 1 thread、答え 1 つあたり 1 秒)確かめる: TCP でつなぎ、confirm(OEP! の答え、探りの規則)と
    fn 0 の describe を送り、describe の unit_id が TXT の unit_id と同じかを見る。session は開かない。discovery.check(f) は 1 つの
    instance の確かめ(None か、言ったこと)。oep find は確かめたものだけを並べ、外したものはその接続先と言ったことを stderr に
    警告する(1 つも残らなければ「no verified probe」、終了コード 1)。--no-verify はそのまま並べ、--verify-timeout S で待ちを変える。find_unit(つまり tcp:UNIT_ID と開き直し)は
    その TXT の unit_id を持つ確かめた最初の instance を取り、ほかは飛ばす(verified=False なら広告された最初のもの)。open_host は開いた経路で confirm と describe の
    unit_id を今までどおり確かめる(確認した: 変わらない)。tests/test_virtual_bench_announce.py に、同じ process の responder で
    OEP でない TCP のサービス(自分の unit_id と bench の unit_id で)と、bench の port を違う TXT の unit_id で広告するものを足した。
    tests/test_wifi_and_tcp_discovery.py も合わせた(作った instance は --no-verify / verified=False を使い、違う TXT は find_unit
    の確かめで外れ、open_host 自身の unit_id の確かめも引き続き試す)。
  • (EN) virtual_bench_serve --tcp speaks length frames by default (as a probe's TCP transport, transports §1;
    ch32rv's tests had to pass --framing length). --framing cobs stays as an explicit option that emulates a serial
    port over the socket (COBS frames and the port's raw bytes, one connection at a time, as before); the pty is
    unchanged. Breaking for a caller that relied on COBS from a bare --tcp: pass --framing cobs. Help text and
    README (EN / JA) follow; tests that named the old default updated (oep-client-js's test/virtual-bench.js already
    names its framing).
  • (JA) virtual_bench_serve --tcp は既定で length のフレームを話す(probe の TCP の経路と同じ、transports §1。ch32rv のテストは
    --framing length を付ける必要があった)。--framing cobs は指定したときだけの選択肢として残り、socket の上でシリアルの口を
    まねる(COBS のフレームと口の生のバイト、今までどおり一度に 1 つの接続)。pty は変わらない。--tcp だけで COBS を当てにしていた
    呼び手には壊れる変更: --framing cobs を付ける。help と README(EN / JA)を合わせ、古い既定を名指ししていたテストを直した
    (oep-client-js の test/virtual-bench.js は framing を常に指定している)。
  • (EN) Follow oep-spec 2b17990 .. 9118dc0 (wifi_min_max_frame, optional TCP advertising). Registry and vectors
    synced; README names 9118dc0.
    • probe.config §1.4: a probe with the wifi item answers max_frame 112 or more on every transport
      (wifi_min_max_frame: one set of the longest wifi item, 10 + 3 + 3 + 32 + 64). The virtual bench complies:
      esp32-v003 keeps wifi (max_frame 512), esp32-v003-64 has no wifi item, and a VirtualProbe that declares wifi
      below 112 raises ValueError (VirtualProbe.has_wifi).
    • ProbeConfig.set refuses a request longer than the transport's max_frame before sending (ValueError naming the size
      and max_frame; for a wifi set on a probe below 112 it names the §1.4 rule; no passphrase in the message).
      config.WIFI_MIN_MAX_FRAME.
    • The new vector (the longest wifi set, 112 bytes, corr 0x7C) passes both ways; the wifi vector count is 9.
    • transports §3: a TCP probe need not advertise; the not-found messages of tcp:UNIT_ID and tcp://HOST say to name
      it as tcp://HOST:PORT.
    • tests: test_config's paging test sets its slots one per request (the 64-byte frame); new tests for the refusal and
      the profiles.
  • (JA) oep-spec 2b17990〜9118dc0 に合わせた(wifi_min_max_frame、TCP の知らせは任意)。 registry と vector を同期し、
    README は 9118dc0 を名乗る。
    • probe.config §1.4: wifi の項目を持つ probe はどの経路でも max_frame 112 以上を答える(wifi_min_max_frame: いちばん長い
      wifi の項目 1 つの set、10 + 3 + 3 + 32 + 64)。virtual bench を合わせた: esp32-v003 は wifi を持つまま(max_frame 512)、
      esp32-v003-64 は wifi の項目を持たず、112 未満で wifi を宣言する VirtualProbe は ValueError(VirtualProbe.has_wifi)。
    • ProbeConfig.set は経路の max_frame より長い要求を送る前に断る(大きさと max_frame を書いた ValueError。112 未満の probe への
      wifi の set なら §1.4 の規則も書く。パスフレーズは入れない)。config.WIFI_MIN_MAX_FRAME。
    • 新しい vector(いちばん長い wifi の set、112 byte、corr 0x7C)は両方向で通る。wifi の vector は 9 個。
    • transports §3: TCP の probe は知らせなくてよい。tcp:UNIT_ID と tcp://HOST で見つからないときの文は tcp://HOST:PORT で
      指すよう書く。
    • テスト: test_config の分けて読むテストはスロットを 1 要求に 1 つずつ置く(64 byte のフレーム)。断りと profile のテストを足した。
  • (EN) virtual_bench_serve --tcp serves several connections at once (length framing).
    • Up to --tcp-connections N (default 3, as the reference probe, oep-probe-arduino implementation-limits §6);
      one more is accepted and closed at once. Each connection is a transport of its own (transports §1): its own confirm
      and revision in use, answers on it, notifications only on the connection their fn's subscribe came on (core §11.4).
      All share one probe (one Endpoint, transports §3): the session, lock and resend table - an open from another
      connection while a session holds the lock is refused locked, lock_state shows it from every connection; a closed
      connection leaves them, and an open with the same session id from another connection takes the session back and
      its notifications with it. ch32rv found a second connection waiting behind the first.
    • --once ends the program when no connection is left after one was served (with one connection: as before).
      --framing cobs (a serial port) still serves one connection at a time; --tcp-connections with it is an error.
      pty mode unchanged. A connection that takes nothing for 2 s while answers wait is closed; notifications past
      2 x max_frame waiting on one are dropped (core §11.4). The reboot / lose / wifi-air stdin commands and
      --announce work as before; the TCP transport is in describe from the start.
    • Endpoint.handle(..., link=) names the connection a request came on (default: the transport index);
      Endpoint.pushes_to() gives each notification with its connection; revision_in_use is kept per connection.
    • New tests/test_virtual_bench_serve_tcp_connections.py: two clients at once (lock, locked, lock_state, end, the
      other opens), a fourth connection closed (and --tcp-connections 1), one client dropping while another goes on, a
      session resumed from a new connection, notifications routed and moved, --once with two, stdin commands.
  • (JA) virtual_bench_serve --tcp は同時に複数の接続に答える(length の framing)。
    • 最大 --tcp-connections N 個(既定 3。参照の probe、oep-probe-arduino implementation-limits §6 と同じ)。それを超える接続は
      受けてすぐ閉じる。どの接続も別の経路(transports §1): confirm と使っている revision は接続ごと、応答はその接続に、通知は
      その fn の subscribe が来た接続にだけ(core §11.4)。probe は 1 つ(1 つの Endpoint、transports §3)でセッション、ロック、
      送り直しの表を共有する: セッションがロックを持つ間、別の接続の open は locked で断られ、lock_state はどの接続からも見える。
      接続が閉じてもそれらは残り、別の接続からの同じ session id の open がセッションを取り戻し、通知もその接続に移る。ch32rv が、
      2 つ目の接続が 1 つ目の後ろで待たされることを見つけた。
    • --once は、接続を 1 つ受けた後に接続が 1 つも無くなったら終わる(接続が 1 つなら今までと同じ)。--framing cobs
      (シリアルの口)は今も一度に 1 つの接続に答え、--tcp-connections と一緒なら誤り。pty は変わらない。応答が待っているのに
      2 s 何も受け取らない接続は閉じる。1 つの接続で 2 x max_frame を超えて待つ通知は捨てる(core §11.4)。stdin の reboot /
      lose / wifi-air と --announce は今までどおり。TCP の経路は始めから describe に載る。
    • Endpoint.handle(..., link=) は要求が来た接続を名指す(既定は経路の index)。Endpoint.pushes_to() は通知をその接続と
      組にして返す。revision_in_use は接続ごとに持つ。
    • 新しい tests/test_virtual_bench_serve_tcp_connections.py: 同時の 2 つの client(ロック、locked、lock_state、end、もう一方の
      open)、4 つ目の接続が閉じられる(と --tcp-connections 1)、1 つの client が落ちても別の client は続く、新しい接続からの
      セッションの取り戻し、通知の行き先とその移動、2 つの接続での --once、stdin のコマンド。
  • (EN) virtual_bench_serve announces itself (DNS-SD), discovery queries every interface, all 8 wifi vectors checked.
    • virtual_bench_serve --announce (with --tcp; length framing, now the default with it) answers mDNS / DNS-SD
      queries for _oep._tcp (transports §3): PTR -> instance OEP virtual <unit_id> <port>, SRV (the port, host
      oep-virtual-<unit_id>-<port>.local.), TXT unit_id=<unit_id>, A (the --listen address, 127.0.0.1 by default;
      0.0.0.0: every announcing interface's). python-zeroconf with the mdns extra, else new module virtual_bench_mdns's
      minimal responder: legacy unicast queries (source port not 5353) answered to the sender with their ID and question,
      TTL 10; queries from 5353 answered on the group; one announcement at start, a goodbye at the end. New options
      --listen ADDR, --unit-id ID (virtual_bench.unit_id_of), --announce-on ADDR (default every interface,
      loopback where it can join), --announce-engine. PORT n comes once the announcement is up. README: how a CI on one
      host uses it (the querier and the responder on one machine).
    • discovery's minimal engine sends its query out of every IPv4 interface (IP_MULTICAST_IF per address,
      discovery.interface_addresses, send_query) and its 5353 socket joins the group on each: a query sent once left by
      one adapter only (ch32rv on Windows: WSL's vEthernet), and a probe on the Wi-Fi adapter never heard it. The zeroconf
      engine names InterfaceChoice.All (its default).
    • tests/test_vectors.py routes the wifi vectors by the state's "items has wifi", not the name: "pass_len 0xFF for an
      index with no entry" and "a 7-byte passphrase" are now checked (the latter: ValueError before sending, the answer
      malformed); a test asserts the 8 vectors, corr 0x74-0x7B.
    • New tests/test_virtual_bench_announce.py: oep find and discovery.find_unit (both engines) and tcp:UNIT_ID find
      the announcing virtual bench on this host (each responder engine), the raw legacy unicast and multicast answers, a
      responder on loopback only found by the per-interface query and not by one send by the default route; skipped where
      multicast does not loop back.
  • (JA) virtual_bench_serve が自分を広告する(DNS-SD)、discovery はすべてのインターフェースで問う、wifi のベクタ 8 本をすべて確かめる。
    • virtual_bench_serve --announce(--tcp と。framing は length、これと一緒なら既定)は _oep._tcp の mDNS / DNS-SD の問い合わせに
      答える(transports §3): PTR -> instance OEP virtual <unit_id> <port>、SRV(port と host oep-virtual-<unit_id>-<port>.local.)、
      TXT unit_id=<unit_id>、A(--listen のアドレス、既定 127.0.0.1。0.0.0.0 なら広告するインターフェースすべてのもの)。mdns の
      extra があれば python-zeroconf、無ければ新しいモジュール virtual_bench_mdns の最小の応答器: legacy unicast の問い合わせ(送り元の
      port が 5353 でない)には送り手へ ID と問いを付けて TTL 10 で答え、5353 からの問い合わせには group に答える。始めに一度広告し、
      終わりに goodbye を出す。新しいオプション --listen ADDR、--unit-id ID(virtual_bench.unit_id_of)、--announce-on ADDR
      (既定はすべてのインターフェース、入れるなら loopback も)、--announce-engine。PORT n は広告が出てから出る。README: 1 台の上の
      CI での使い方(問う側と答える側が同じ機械)。
    • discovery の最小の engine は問い合わせを IPv4 のインターフェースそれぞれから送り(アドレスごとに IP_MULTICAST_IF、
      discovery.interface_addresses、send_query)、5353 のソケットもそれぞれで group に入る: 一度の送信は 1 つのアダプタからしか
      出ず(Windows の ch32rv: WSL の vEthernet)、Wi-Fi のアダプタの probe には届かなかった。zeroconf の engine は InterfaceChoice.All
      (既定)を明示する。
    • tests/test_vectors.py は wifi のベクタを名前でなく state の「items has wifi」で振り分ける: 「pass_len 0xFF for an index with no
      entry」と「a 7-byte passphrase」も確かめる(後者は送る前に ValueError、答えは malformed)。8 本(corr 0x74〜0x7B)あることを試験で確かめる。
    • 新しい tests/test_virtual_bench_announce.py: oep find と discovery.find_unit(両方の engine)と tcp:UNIT_ID が、この host で
      広告する仮想ベンチを見つける(応答器の engine ごと)。生の legacy unicast と multicast の答え。loopback だけで答える応答器は
      インターフェースごとの問い合わせで見つかり、既定の経路への一度の送信では見つからない。multicast が戻らない所では skip。
  • (EN) oep-spec c2b8007 .. 62c1988: the wifi item and TCP discovery. README names 62c1988. Registry and vectors
    synced (tools/sync_registry.sh; the wifi vectors, corr 0x74-0x7B, pass both ways).
    • Breaking (wire): unset's len is the key's length (probe.config §2, as the new vector "unset: wifi entry 0" has
      it: 01 01 08 00); it counted the tag too. config.Removal.encoded() and the virtual bench (a len other than the
      tag's key length is malformed) follow; oep-probe-arduino still counts the tag and key (OepConfig.cpp unset).
    • probe.config §1.4 / §3.3: config.Wifi(index, ssid, passphrase) - passphrase None (open), config.KEEP (pass_len
      0xFF: the entry's own; get's form) or 8-63 printable / 64 hex (else ValueError, without the passphrase); its repr and
      shown() say set / none, never the passphrase. Declared.wifi_max, State.wifi = WifiState(state, entry, reason, rssi, ipv4) (off / connecting / connected / waiting; reason none / not-found / auth / no-address / other).
      same_items / apply compare a wifi item as get shows it (host guide §15.1). config.wifi_from_env() reads
      OEP_WIFI_SSID_<n> / OEP_WIFI_PASS_<n>.
    • oep config wifi <probe> --index N --ssid S [--pass-prompt | --pass-env VAR | --open] [--save] (neither: keep the
      entry's passphrase), --from-env [--force], oep config wifi-unset <probe> --index N; show / state print the
      entries (passphrase set / none) and the link (state, entry, reason, rssi, ip), --json too. Nothing prints a
      passphrase.
    • The virtual bench: esp32-v003 declares the wifi item (wifi_max 4); the passphrase write-only (get 0xFF, a set
      with 0xFF keeps it, malformed without the entry), an index at or past wifi_max unsupported with the tag as
      received, an SSID with 0x00 unsupported, the hash from get's form and a token that changes with a passphrase; a
      simulated link (Endpoint.wifi_set_air, wifi_join_ms, wifi_rssi, wifi_ip): entries in index order against
      the networks in range, the entry in use changing joins again, another change keeps the link.
      virtual_bench_serve --wifi-air SSID[=PASS] --wifi-join-ms --wifi-ip and the stdin command wifi-air.
      Endpoint.hash_fn: a test's own hash (the vectors name the probe's).
    • transports §3 / host guide §4.1: new module discovery (DNS-SD _oep._tcp over mDNS: browse, find_unit,
      port_of; python-zeroconf with the new mdns extra, else a minimal query of its own), oep find [--json]
      (unit_id, host, port, address). open_host("tcp://HOST") without a port takes the SRV port DNS-SD finds for that
      host (none: LookupError naming tcp://HOST:PORT - no port is fixed); open_host("tcp:UNIT_ID") opens the probe found
      by its TXT unit_id, checks describe's (UnitIdMismatch) and browses again when it reopens.
    • oep linktest --timeout defaults to 3 s over TCP (Wi-Fi retransmissions stall answers 1-4 s), 0.3 s elsewhere
      (linktest.default_timeout); tests/hw's port_speed uses the same.
    • KeptSession.__del__ no longer raises ImportError at interpreter exit (fcntl / msvcrt imported with the module,
      __del__ swallows what a shutdown leaves).
    • tests/hw: TCP boards - esp32-pico-d4-50029191fe34-tcp (the ATOM, tcp:50029191fe34 or OEP_HW_ATOM_TCP) and
      any tcp:<unit_id> / tcp://<host>[:<port>] in OEP_HW_BOARDS (kind tcp: never flashed, no model check). A new
      wifi test sets and saves the entries from OEP_WIFI_SSID_<n> / OEP_WIFI_PASS_<n> (only those that differ),
      waits for the link (OEP_HW_WIFI_WAIT_S) and opens tcp:<unit_id> when DNS-SD finds it; results record
      OEP_WIFI_* as (set) and no SSID, passphrase or address. The virtual board gets the same networks in range
      through the serve's stdin. New tests/test_wifi_and_tcp_discovery.py. README / tests/hw README (EN / JA).
  • (JA) oep-spec c2b8007〜62c1988: wifi の項目と TCP の見つけ方。 README は 62c1988 を名乗る。registry とベクタを写した
    (tools/sync_registry.sh。wifi のベクタ corr 0x74〜0x7B は両向きとも通る)。
    • 破壊的(wire): unset の len は key の長さ(probe.config §2。新しいベクタ "unset: wifi entry 0" の 01 01 08 00 のとおり)。
      これまでは tag も数えていた。config.Removal.encoded() と仮想ベンチ(tag の key の長さと違う len は malformed)が従う。
      oep-probe-arduino はまだ tag と key を数えている(OepConfig.cpp の unset)。
    • probe.config §1.4 / §3.3: config.Wifi(index, ssid, passphrase) - passphrase は None(開いたネットワーク)、config.KEEP(pass_len
      0xFF: その entry のもの。get の形)、8〜63 の印字できる文字か 16 進 64 文字(ほかは ValueError。passphrase は載せない)。repr と
      shown() は set / none と言うだけで passphrase を出さない。Declared.wifi_max、State.wifi = WifiState(state, entry, reason, rssi, ipv4)(off / connecting / connected / waiting。reason は none / not-found / auth / no-address / other)。same_items /
      apply は wifi の項目を get の見せる形で比べる(host ガイド §15.1)。config.wifi_from_env() は OEP_WIFI_SSID_<n> /
      OEP_WIFI_PASS_<n> を読む。
    • oep config wifi <probe> --index N --ssid S [--pass-prompt | --pass-env VAR | --open] [--save](どれも無ければ entry の
      passphrase を保つ)、--from-env [--force]、oep config wifi-unset <probe> --index N。show / state は entry(passphrase
      set / none)とつながり(state、entry、reason、rssi、ip)を出す(--json も)。passphrase はどこにも出さない。
    • 仮想ベンチ: esp32-v003 が wifi の項目(wifi_max 4)を宣言する。passphrase は書くだけ(get は 0xFF、set の 0xFF は保つ、entry が
      無ければ malformed)、wifi_max 以上の index は受け取ったままの tag で unsupported、0x00 を含む ssid は unsupported、hash は get の
      形と passphrase が変わるたびに変わる token から作る。つながりの模擬(Endpoint.wifi_set_air、wifi_join_ms、wifi_rssi、
      wifi_ip): entry を index の順に届くネットワークと突き合わせ、使っている entry が変わればつなぎ直し、ほかの変更ではつながりを保つ。
      virtual_bench_serve --wifi-air SSID[=PASS] --wifi-join-ms --wifi-ip と stdin の命令 wifi-air。Endpoint.hash_fn: 試験が
      決める hash(ベクタは probe の hash を名指す)。
    • transports §3 / host ガイド §4.1: 新しいモジュール discovery(mDNS の DNS-SD _oep._tcp: browse、find_unit、port_of。
      新しい mdns の extra で python-zeroconf、無ければ自前の最小の問い合わせ)、oep find [--json](unit_id、host、port、アドレス)。
      port の無い open_host("tcp://HOST") はその host に DNS-SD が見つけた SRV の port を使う(無ければ tcp://HOST:PORT を示す
      LookupError。決まった port は無い)。open_host("tcp:UNIT_ID") は TXT の unit_id で見つけた probe を開き、describe のものを
      確かめ(UnitIdMismatch)、開き直すときは browse し直す。
    • oep linktest --timeout の既定は TCP で 3 秒(Wi-Fi の再送で応答が 1〜4 秒止まる)、ほかは 0.3 秒(linktest.default_timeout)。
      tests/hw の port_speed も同じ。
    • KeptSession.__del__ はインタプリタの終わりに ImportError を出さない(fcntl / msvcrt はモジュールと一緒に import し、
      __del__ は終わりぎわの例外を飲む)。
    • tests/hw: TCP のボード - esp32-pico-d4-50029191fe34-tcp(ATOM。tcp:50029191fe34 か OEP_HW_ATOM_TCP)と、OEP_HW_BOARDS
      の tcp:<unit_id> / tcp://<host>[:<port>](kind tcp: 焼かない、model を照合しない)。新しい wifi の試験は OEP_WIFI_SSID_<n>
      / OEP_WIFI_PASS_<n> の entry(違うものだけ)を set と save し、つながりを待ち(OEP_HW_WIFI_WAIT_S)、DNS-SD が見つければ
      tcp:<unit_id> で開く。結果は OEP_WIFI_* を (set) とだけ記録し、ssid、passphrase、アドレスを入れない。仮想のボードには
      serve の stdin で同じネットワークを届かせる。新しい tests/test_wifi_and_tcp_discovery.py。README / tests/hw README(EN / JA)。
  • (EN) oep-spec 283e5b5 .. f8bb2de (the external review re-check of 2026-10-07 and the fixes after it). README names
    f8bb2de. Registry and vectors synced (tools/sync_registry.sh): resend_max is gone (the host decides its resends:
    this client's - once after the wait, at once up to 3 times after a broken frame on a held serial port - stay),
    interface_name_max_bytes 64 -> 48 (names.MAX_NAME, a list entry fits the smallest max_frame).
    • transports §2: a frame may be split over writes, no sender pauses probe_frame_gap_ms inside one off TCP. Checked: the
      client writes each batch of frames whole in one write (serial, vendor bulk; HID reports back to back; TCP sendall) and
      its length-frame reader drops a frame stalled for probe_frame_gap_ms only off TCP; the virtual bench's serial port
      takes a frame in any pieces and writes its answers whole. Nothing changed.
    • transports §3: a closed transport does not end the session. The virtual bench's TCP (virtual_bench_serve --tcp)
      already kept it; now also documented and tested (the session, lock and resend table stay until the lease runs out,
      the next connection's open with the same id takes it back) and the notifications made while no connection is open
      are dropped instead of going to the next connection.
    • A host run again ends the session its previous run left (host guide §5, f8bb2de): link.open_host(..., keep_session=True) (the default, so the oep command and tests/hw) keeps the session id in
      <dir>/<unit_id>.session ($OEP_SESSION_DIR, else $XDG_RUNTIME_DIR/oep-client, else the user's cache directory),
      locked while the host runs; the first open opens the kept id and ends it at once (Host.end_previous), then opens
      as usual. A file another running host holds is left alone; an x- unit_id keeps nothing. New module
      kept_session (KeptSession, Host.kept).
    • core §7.5 / §1.2: a probe with channels declares channels, numbers 0 .. channels - 1, absent = none. The virtual
      bench's endpoint reads an absent channels as 0 (it took every channel an interface named); every profile declares
      it and names no channel past it (tested).
    • fixture §3 (4a4631a): the virtual bench's i2c-target counts at most 1 error a write (over max_length into a full
      queue: 1, was 2).
    • oep-if-link / host guide §17 (44aa045): the sink count is max_frame - 12, which this client already sends
      (core.link_sink_size); nothing changed.
    • The tests run with OEP_SESSION_DIR in a temporary directory (tests/conftest.py; tests/hw keeps the real one).
      New tests/test_spec_283e5b5.py. README / tests/hw README (EN / JA).
  • (JA) oep-spec 283e5b5〜f8bb2de(2026-10-07 の外部レビューの再確認と、その後の直し)。README は f8bb2de を名乗る。registry と
    ベクタを写した(tools/sync_registry.sh): resend_max は無くなった(送り直しは host が決める。この client の送り直し - 待ちの後に
    1 回、持っているシリアルの口で壊れたフレームが来たらすぐ 3 回まで - はそのまま)。interface_name_max_bytes は 64 -> 48
    (names.MAX_NAME。list の項目 1 つがいちばん小さい max_frame に収まる)。
    • transports §2: フレームは書き込みに分けてよく、TCP 以外では送る側はフレームの途中で probe_frame_gap_ms 止めない。確かめた: client は
      フレームのまとまりを 1 回の書き込みで丸ごと書き(シリアル、vendor bulk。HID は report を続けて、TCP は sendall)、長さつきフレームの
      読み手が probe_frame_gap_ms 止まったフレームを捨てるのは TCP 以外だけ。仮想ベンチのシリアルの口はフレームをどう分けても受け、答えは
      丸ごと書く。変更なし。
    • transports §3: 経路が閉じてもセッションは終わらない。仮想ベンチの TCP(virtual_bench_serve --tcp)はもとから保っていた。それを
      文書と試験にした(セッション、ロック、送り直しの表は lease が切れるまで残り、次の接続の同じ id の open が取り戻す)。接続が無い間の
      通知は、次の接続に送らずに捨てるようにした。
    • 走り直す host は、前の実行が残したセッションを終える(host ガイド §5、f8bb2de): link.open_host(..., keep_session=True)(既定。
      oep の命令と tests/hw も)は、セッションの id を <dir>/<unit_id>.session($OEP_SESSION_DIR、無ければ
      $XDG_RUNTIME_DIR/oep-client、無ければ利用者の cache の場所)に残し、host が動く間は錠を掛ける。最初の open は残した id で open
      してすぐ end し(Host.end_previous)、それからふつうに開く。ほかの動いている host が持つファイルには触れない。x- の unit_id は
      何も残さない。新しいモジュール kept_session(KeptSession、Host.kept)。
    • core §7.5 / §1.2: channel を持つ probe は channels を付け、番号は 0〜channels − 1、無ければ 0 個。仮想ベンチの endpoint は
      channels が無ければ 0 と読む(これまではインターフェースが名指す channel をすべて取っていた)。どの profile も付け、それを越える
      channel を名指さない(試験した)。
    • fixture §3(4a4631a): 仮想ベンチの i2c-target は、書き込み 1 回に errors を多くても 1 数える(max_length を超えて列もあふれた書き込みは
      1。これまでは 2)。
    • oep-if-link / host ガイド §17(44aa045): sink の count は max_frame − 12 で、この client はもとからそれを送る(core.link_sink_size)。
      変更なし。
    • 試験は OEP_SESSION_DIR を一時の場所にして走る(tests/conftest.py。tests/hw は本当の場所のまま)。新しい
      tests/test_spec_283e5b5.py。README と tests/hw の README(EN / JA)。
  • (EN) Breaking (API, CLI): the fake probe is the virtual bench, with no aliases (pre-freeze). The virtual bench is a
    probe, the targets behind it and the fixture wiring, modelled after the real jigs: the environment host software is
    tested in. "Bench" alone still means the real HIL jigs. Renamed: module oep_client.fake -> oep_client.virtual_bench,
    fake_capture -> virtual_bench_capture, fake_serial -> virtual_bench_serial, fake_serve ->
    virtual_bench_serve (python -m oep_client.virtual_bench_serve, its stderr lines start virtual_bench_serve:);
    FakeProbe -> VirtualProbe, FakeTarget -> VirtualTarget, FakeCapture -> VirtualCapture, FakeGroup ->
    VirtualGroup, FakeSerialPort -> VirtualSerialPort, FakeSerialStream -> VirtualSerialStream; oep dump --fake <profile> -> oep dump --virtual <profile>; tests/hw's board kind fake -> virtual, its board
    fake-esp32-v003 -> virtual-esp32-v003 (Board.fake_profile -> virtual_profile, Run.fake -> Run.virtual_bench);
    test files tests/test_fake_*.py -> tests/test_virtual_bench_*.py. README / tests/hw README (EN / JA) say virtual
    bench / 仮想ベンチ. The entries below name the old names as they were then.
  • (JA) 破壊的(API、CLI): 偽の probe は仮想ベンチになった(凍結前なので別名は置かない)。仮想ベンチは、probe とその先の target、
    治具の配線を実際の治具に合わせて作ったもので、host のソフトウェアを試す環境である。「ベンチ」とだけ書けば、今までどおり実機の HIL の
    治具を指す。名前の変更: モジュール oep_client.fake -> oep_client.virtual_bench、fake_capture -> virtual_bench_capture、
    fake_serial -> virtual_bench_serial、fake_serve -> virtual_bench_serve(python -m oep_client.virtual_bench_serve。
    stderr の行は virtual_bench_serve: で始まる)。FakeProbe -> VirtualProbe、FakeTarget -> VirtualTarget、FakeCapture ->
    VirtualCapture、FakeGroup -> VirtualGroup、FakeSerialPort -> VirtualSerialPort、FakeSerialStream ->
    VirtualSerialStream。oep dump --fake <profile> -> oep dump --virtual <profile>。tests/hw の board の kind fake -> virtual、
    board fake-esp32-v003 -> virtual-esp32-v003(Board.fake_profile -> virtual_profile、Run.fake -> Run.virtual_bench)。
    試験のファイル tests/test_fake_*.py -> tests/test_virtual_bench_*.py。README と tests/hw の README(EN / JA)は仮想ベンチと書く。
    下の項目は、その時の古い名前のまま。
  • (EN) Breaking (wire and API): oep-spec 7688c49 .. 0f455a0, the rule review of 2026-10-07 (§2 applied, §7).
    README names 0f455a0. Registry and vectors synced (tools/sync_registry.sh; probe_config_hash.json gone).
    The fake (endpoint, fake, fake_capture) answers as the new text says - other clients test against it:
    • core: no ignored TLV - an unknown non-critical request TLV is ignored silently, an unknown critical one refused
      unsupported (its tag as received); a TLV the probe implements is checked the same with or without bit 7 (another
      length, longer too, or an excluded value: malformed; an unused or unhandled value: unsupported with the tag as
      received); a non-repeating tag twice: the first is used; tags 0x00 / 0x7F are no tags; booleans read non-zero as
      true (open's force); request text is not checked (owner keeps its 1-32 bytes). Refusals: the header, the resend
      table, the session, then every check before any change and any one reason that applies. The resend table is
      (corr, answer): no corr_reused, no CRC-32 (m.CORR_REUSED, m.TAG_IGNORED, m.TAG_INVALID, Tail.ignored /
      more_ignored / may_have_ignored, endpoint.ignored_tlv gone). list takes first(u16) alone
      (catalog.pack_list_request(first); core.list_entries(hst, name, exact) filters on the host); fn 0's describe
      loses implementation, reserved, profile, resets_on_open and discoverable (chip / model free text;
      catalog.IMPLEMENTATION, Description.implementation gone); unavailable's payload is cause, channel, fn
      (Unavailable.holder_fn / holder_kind gone, Unavailable.fn new); an ops value is base and a bitmap of 1 byte or
      more not past op 0xFF; resource numbers +1, skipping those in use; link source len max_frame - 7
      (core.link_size), a sink request at most max_frame - 12 (core.link_sink_size).
    • debug: attach, scan and riscv-dm's reset answer within max_op_ms, which the host counts as their argument time
      (Wire.attach_ms(), scan_ms(), RiscvDm.reset_ms(); the 13 debug limits are gone, core.FALLBACK_MAX_OP_MS for a
      probe that declares none); riscv-dm reset answers status flags pc (no method, no attempts: RiscvDm.reset(confirm) -> (flags, pc), reset_halt() -> dpc); run: a preparation that fails answers stopped 3 not_run
      (RunResult.not_run; the fake: FakeTarget.fail_regs, a running hart), timeout_ms 0 is not refused; DATA0 / DATA1
      restore is riscv-dm's: read_register / write_register save DATA0 first (RiscvDm.data_saved) and resume /
      step / run write it back first (restore_data); target_id scheme dmi_7f (Wire.SCHEME_DMI_7F,
      targets.SCHEME_DMI_7F); a count > 0 scan's skip is not looked at. common: read from 3 with arg > 0xFF and write
      count 0 are not refused. console: no send_queue (the queue is the probe's own size; Console.send_queue gone,
      ConsoleIO writes frame-sized chunks); the console's reads follow the order rule (no 20 ms).
    • capture: configure's TLVs follow core §2.3 alone; the answer loses timing and rate_accuracy (Config.jitter_kind,
      jitter_ns, rate_measured, rate_ppm, ignored gone); describe's mode is mode max_samples max_segments,
      channels max alone, no rate_list / rate_limit / max_read / segment_ring / frontend_shared, capture-group tracks
      alone (the fake keeps widths, ring, max_read, max_tracks and budgets in Offered.inner).
    • fixture: gpio mode 7 gone; drive is a u8 level (0xFF the default; Drive.level(n) / Drive.default(),
      DriveLevels.at_most(ma); Drive.max_ma, Gpio.read_state, GpioRead, INPUT_PULLUP_PULLDOWN gone; Gpio.set
      returns None and sends the drive critical): a level past drive_levels, or any drive without them, is refused
      unsupported; uart status is baud and format (UartStatus(baud, format)); i2c-target has one form -
      I2cTarget.configure(address), a write with data is one frame (cut at max_length, errors + 1), reads from
      preload_tx(data) slots (it answers nothing) or 0xFF, status() -> I2cStatus(state, queued, rx_frames, tx_slots, errors), internal_pullups (arm_rx, reset, the modes, pullup_ohms gone; stretch stays optional);
      spi-target has no reset (SpiTarget.reset gone) and counts errors once a transfer.
    • probe.config: the idle item is 4 bytes, channel mode drive(u8); an item of another length is malformed whether
      critical or not; label's rule is its length; slot without lock and boot_reset (config.Slot loses lock /
      boot_reset; retry_ms not looked at on a host slot), slot_state 0 connected / 1 absent (SlotState(slot, state, connection, last_try_at_ns)), a host checks the target with connections' tid (oep config show prints it); bind is
      one stream - config.Bind(port=, stream=("slot", n) | ("uart", fn)), port kind id - whose port position stays
      during a session and carries on after it (BindState(port, flow); modes, mixed lines and Declared.bind_modes
      gone); the hash is the probe's own (config.hash_of / canonical gone: config.same_items,
      ProbeConfig.needs_save(), ProbeConfig.apply(wanted, save) - host guide §15); storage_hash is get's hash when the
      saved settings became current; at boot disable and idle before any other item. oep config bind --stream takes
      one stream (no --mode / --select), oep config slot loses --lock / --boot-reset, --drive-ma picks a
      level from the probe's drive_levels; fake_serve --bind N (the N-th --slot), --boot-reset gone.
    • link: port_speed is the handshake, baud step verify_ms (no port, no idle_ms: raise_speed loses idle_ms /
      port, linktest.matrix loses port); the probe goes back after verify_ms, after port_speed_idle_ms with no good
      frame, or at the session's end (the fake: no broken-candidate reverts; port_speed_tolerance_pct). Host guide §17.3.2
      item 4 (Q8): at a raised rate in use the first wait ends well inside port_speed_idle_ms (link.RAISED_FIRST_WAIT_S,
      never under core §4.4's floor) and the request goes again at the raised rate before the link falls back to the
      boot speed (confirms up to port_speed_idle_ms + host_wait_add_ms). Host guide §8 (Q6): on a held serial port a
      broken frame while an answer is awaited is resent at once with the same corr, up to link.BROKEN_RESENDS (3)
      times while frames keep arriving; a wait with nothing allows the one resend. plan_apply sends role_assignment
      plain. restart's host procedure is the host guide's §5.2 (behaviour kept; host.RESTART_AFTER_ANSWER_S = 0.1).
    • oep pins reads under pull-up and pull-down only (host guide §19.1: pins.PULLED_UP gone, a weak pull-up is
      floating). tests/hw: the config test compares items (same_items, needs_save) instead of computing the hash;
      i2c / spi target tests configure again instead of reset; capture tests read the new declarations.
    • The vectors are oep-spec 85170c4's (the registry is 0f455a0's, unchanged): "rvswd scan: count > 0 with skip" is
      answered as the listed scan (debug §1). tests/test_vectors.py keeps TEXT_OVER_VECTOR (empty) for a vector the
      text corrects. The fake also has Endpoint.lose(connection=None) and fake_serve's stdin line lose [CONNECTION]
      (for ch32rv): the connection's line lost for good - closed, its streams marked link-lost and closed with detail 4,
      no_connection afterwards; an at-boot slot attaches again at its next retry, its console back under the same
      stream number. A new bind starts its port's position when it is set (a stream that comes later: from its start);
      a refused capture-group bind changes nothing; SwdWire.FLAGS is the swd wire's.
  • (JA) 破壊的(wire と API): oep-spec 7688c49〜0f455a0、2026-10-07 の規則の見直し(§2 の適用、§7)。 README は
    0f455a0 を名乗る。registry とベクタを同期(tools/sync_registry.sh。probe_config_hash.json は無くなった)。偽の
    probe(endpoint、fake、fake_capture)は新しい文のとおりに答える(ほかのクライアントがこれで試験する):
    • 本体: ignored の TLV は無い - 知らない非 critical の要求の TLV は黙って無視し、知らない critical のものは unsupported
      (受け取ったままの tag)で断る。probe が実装する TLV は bit 7 によらず同じに確かめる(長さが違う - 長いのも - か除かれた
      値: malformed。使われていない値や扱わない値: 受け取ったままの tag で unsupported)。繰り返さない tag が 2 つあれば最初の
      ものを使う。tag 0x00 / 0x7F は tag でない。真偽値は 0 でなければ真(open の force)。要求の text は確かめない(owner の
      1〜32 byte は残る)。断り方: 見出し、送り直しの表、セッション、その後はどれも何も変える前に確かめ、当たった理由のどれか
      1 つ。送り直しの表は (corr、応答): corr_reused も CRC-32 も無い(m.CORR_REUSED、m.TAG_IGNORED、m.TAG_INVALID、
      Tail.ignored / more_ignored / may_have_ignored、endpoint.ignored_tlv は削除)。list は first(u16) だけ
      (catalog.pack_list_request(first)。core.list_entries(hst, name, exact) は host で絞る)。fn 0 の describe から
      implementation、reserved、profile、resets_on_open、discoverable が消える(chip / model は自由な text。
      catalog.IMPLEMENTATION、Description.implementation は削除)。unavailable の payload は cause、channel、fn
      (Unavailable.holder_fn / holder_kind は削除、Unavailable.fn を追加)。ops の値は base と 1 byte 以上の bitmap で op
      0xFF を越えない。資源の番号は +1 で、使用中の番号を飛ばす。link の source の len は max_frame − 7(core.link_size)、
      sink の要求は多くても max_frame − 12(core.link_sink_size)。
    • debug: attach、scan、riscv-dm の reset は max_op_ms のうちに答え、host はそれを引数の時間として数える
      (Wire.attach_ms()、scan_ms()、RiscvDm.reset_ms()。debug の 13 の限界の値は無い。宣言の無い probe には
      core.FALLBACK_MAX_OP_MS)。riscv-dm の reset の応答は status flags pc(method も attempts も無い:
      RiscvDm.reset(confirm) -> (flags, pc)、reset_halt() -> dpc)。run: 準備が失敗したら stopped 3 not_run
      (RunResult.not_run。偽物: FakeTarget.fail_regs、走っている hart)、timeout_ms 0 は断らない。DATA0 / DATA1 の
      書き戻しは riscv-dm の規則: read_register / write_register は先に DATA0 を取っておき(RiscvDm.data_saved)、
      resume / step / run は先に書き戻す(restore_data)。target_id の scheme は dmi_7f(Wire.SCHEME_DMI_7F、
      targets.SCHEME_DMI_7F)。count > 0 の scan の skip は見ない。common: from 3 で arg > 0xFF の read と count 0 の
      write は断らない。console: send_queue は無い(列の大きさは probe が決める。Console.send_queue は削除、ConsoleIO は
      フレームの大きさで書く)。コンソールの読みは順序の規則に従う(20 ms は無い)。
    • capture: configure の TLV は core §2.3 だけに従う。応答から timing と rate_accuracy が消える(Config.jitter_kind、
      jitter_ns、rate_measured、rate_ppm、ignored は削除)。describe の mode は mode max_samples max_segments、channels
      は max だけ、rate_list / rate_limit / max_read / segment_ring / frontend_shared は無い。capture-group は tracks だけ(偽物は
      幅、区画の記録、max_read、max_tracks、budget を Offered.inner に持つ)。
    • fixture: gpio の mode 7 は無い。drive は u8 の段の番号(0xFF が既定。Drive.level(n) / Drive.default()、
      DriveLevels.at_most(ma)。Drive.max_ma、Gpio.read_state、GpioRead、INPUT_PULLUP_PULLDOWN は削除。Gpio.set は
      None を返し、drive を critical で送る): drive_levels を越える段と、drive_levels の無い probe への drive は unsupported で
      断られる。uart の status は baud と format(UartStatus(baud, format))。i2c-target は 1 つの形 -
      I2cTarget.configure(address)、データのある書き込み 1 回が 1 フレーム(max_length で切り、errors + 1)、読み出しは
      preload_tx(data) の置き場から(応答は空)か 0xFF、status() -> I2cStatus(state, queued, rx_frames, tx_slots, errors)、internal_pullups(arm_rx、reset、mode、pullup_ohms は削除。stretch は任意の op のまま)。spi-target に
      reset は無く(SpiTarget.reset は削除)、errors は転送 1 回に 1 まで。
    • probe.config: idle の項目は 4 byte、channel mode drive(u8)。長さが形と違う項目は critical によらず malformed。label の
      規則は長さだけ。slot に錠も boot_reset も無い(config.Slot から lock / boot_reset を削除。host のスロットの retry_ms
      は見ない)。slot_state は 0 接続あり / 1 いない(SlotState(slot, state, connection, last_try_at_ns))。target の確かめは
      host が connections の tid で行う(oep config show が出す)。bind はストリーム 1 本 - config.Bind(port=, stream=("slot", n) | ("uart", fn))、port kind id -、口の位置はセッションの間進まず、終われば止めた所から続く(BindState(port, flow)。mode、mixed の行、Declared.bind_modes は削除)。hash は probe が作る(config.hash_of / canonical は削除:
      config.same_items、ProbeConfig.needs_save()、ProbeConfig.apply(wanted, save) - host ガイド §15)。storage_hash は
      保存を今の設定にした時の get の hash。起動時は disable と idle をほかのどの項目より先に。oep config bind --stream は
      1 本(--mode / --select は無い)、oep config slot から --lock / --boot-reset を削除、--drive-ma は probe の
      drive_levels から段を選ぶ。fake_serve --bind N(N 番目の --slot)、--boot-reset は削除。
    • link: port_speed は握手だけ、baud step verify_ms(port も idle_ms も無い: raise_speed から idle_ms / port、
      linktest.matrix から port を削除)。probe が戻るのは verify_ms の後、正常なフレームの無い port_speed_idle_ms の後、
      セッションの終わり(偽物: 壊れた候補では戻らない。port_speed_tolerance_pct)。host ガイド §17.3.2 の 4(Q8): 上げた
      速さで使っている間、最初の待ちは port_speed_idle_ms より十分短く終え(link.RAISED_FIRST_WAIT_S。core §4.4 の下限は
      下回らない)、起動時の速さに戻る前に上げた速さのまま送り直す(戻った後の confirm は port_speed_idle_ms +
      host_wait_add_ms まで)。host ガイド §8(Q6): セッションが持つシリアルの口で、答えを待つ間に壊れたフレームが来たら
      すぐ同じ corr で送り直す - フレームが届き続ける間 link.BROKEN_RESENDS(3)回まで。何も来ない待ちの後は 1 回。
      plan_apply は role_assignment を critical の bit 無しで送る。restart の host の手順は host ガイド §5.2(動きは同じ。
      host.RESTART_AFTER_ANSWER_S = 0.1)。
    • oep pins は pull-up と pull-down だけで読む(host ガイド §19.1: pins.PULLED_UP は削除。弱い pull-up は浮いている
      として扱う)。tests/hw: config の試験は hash を計算せず項目を比べる(same_items、needs_save)。i2c / spi target の
      試験は reset の代わりに configure し直す。capture の試験は新しい宣言を読む。
    • ベクタは oep-spec 85170c4 のもの(registry は 0f455a0 のまま変わらない): 「rvswd scan: count > 0 with skip」は並べた組の
      scan として答える(debug §1)。tests/test_vectors.py は文が直すベクタのための TEXT_OVER_VECTOR(空)を残す。偽物に
      Endpoint.lose(connection=None) と fake_serve の stdin の行 lose [CONNECTION] を足した(ch32rv のため): その connection の
      線をずっと失う - 閉じ、ストリームに link-lost の印を付けて detail 4 で閉じ、以後は no_connection。at boot のスロットは次の
      やり直しでまた attach し、コンソールは同じストリームの番号で戻る。新しい bind は set した時から口の位置を持つ(後から
      来るストリームはその始めから)。断られた capture-group の bind は何も変えない。SwdWire.FLAGS は swd の wire のもの。
  • (EN) Host.restart_probe(wait_s=None, reopen_s=None): oep-if-restart §3 lets a host retry a restarted probe only
    until its restart_max_ms has passed - a wait_s longer than a declared restart_max_ms is now cut to it, and the link
    (reopen_after_restart) is closed when the window passes (the probe is gone). reopen_s is the user's reopen of a
    probe given up that way, asked for beforehand: once the window has passed it is opened again as a new open (confirm
    first, the boot_id checked as ever) for up to that much more; Host.restart_reopened says it was needed. For a host on
    which the device comes back later than the probe can know - WSL, where usbipd attaches the re-enumerated device again
    (bench: an RP2350's restart worked, but its 2.0 s restart_max_ms passed before usbipd attached it, and the rest of
    tests/hw failed). oep restart --reopen-s S. tests/hw: OEP_HW_REOPEN_S passes it to the config test's restart (the
    record says reopen_s / reopened, or the error); a probe that did not come back is marked gone - later tests skip naming
    it, each first trying once to open it again. tests/hw puts back every setting it changes whatever fails
    (Run.restore_settings: the config test's label / disable items and its saves, the capture test's idle items; each
    item removed or set back to its value from before the run, the storage saved again or erased; the probe opened again
    when it went away, once more at the run's end) - the bench's run had left channel 28's disable on the probe. What
    cannot be put back is recorded (_settings.left_on_probe) and printed in the summary with the oep config commands
    that remove it. The hw conftest skips by the hw marker (not the package's keyword), so tests/hw/test_harness.py
    (the restore on the in-process fake) runs in the ordinary suite. Tests: a long wait_s cut, reopen_s on a link and on
    a bare send, the restore after a failed test, nothing saved before (erased), a probe that went away and came back.
    README, tests/hw README (EN / JA: the WSL / usbipd note, where tests/hw changes probe state and how each is put back).
  • (JA) Host.restart_probe(wait_s=None, reopen_s=None): oep-if-restart §3 で host が再起動した probe を待って繰り返せるのは
    restart_max_ms が過ぎるまで - 宣言された restart_max_ms より長い wait_s はそれに切り、待ちが過ぎたら link
    (reopen_after_restart)を閉じる(probe は無くなった)。reopen_s は、そうして無くなったものとした probe を利用者が
    開き直すことを前もって頼むもの: 待ちが過ぎた後、新しく開くのと同じに(最初は confirm、boot_id はいつもどおり確かめる)
    さらにその秒数まで開き直す。要ったかどうかは Host.restart_reopened。probe には分からない遅れで device が戻る host の
    ため - WSL では列挙し直した device を usbipd が付け直す(bench: RP2350 の再起動はできたが、usbipd が付け直す前に
    restart_max_ms の 2.0 s が過ぎ、tests/hw の残りが失敗した)。oep restart --reopen-s S。tests/hw: OEP_HW_REOPEN_S を
    config の試験の再起動に渡す(記録に reopen_s / reopened、またはエラー)。戻らなかった probe は無くなったものとし、後の
    試験はそれを書いて skip する(どれも最初に 1 度開き直してみる)。tests/hw は、何が失敗しても変えた設定をすべて元に戻す
    (Run.restore_settings: config の試験の label / disable の項目と save、capture の試験の idle の項目。項目は取り除くか
    走る前の値に set し直し、保存はもう一度 save するか erase する。probe が無くなっていれば開き直し、走り終わりにもう 1 度)
    • bench の走りでは channel 28 の disable が probe に残っていた。戻せなかったものは記録し(_settings.left_on_probe)、
      まとめにそれを取り除く oep config のコマンドと一緒に出す。hw の conftest は(パッケージ名の keyword でなく)hw の印で
      skip するので、tests/hw/test_harness.py(偽の probe での戻し)は普段の試験で回る。試験: 長い wait_s を切る、link と
      素の send での reopen_s、失敗した試験の後の戻し、走る前に保存が無い(erase)、無くなって戻った probe。README、tests/hw の
      README(EN / JA: WSL / usbipd の注意、tests/hw が probe の状態を変える所とそれぞれの戻し方)。
  • (EN) port_speed's default ceiling is 500000 (oep-spec 32260e5, oep-if-link §3 host obligation 7, host guide
    §17.3.3): the default candidate stays 500000 alone (link.DEFAULT_CANDIDATES), and a rate above link.DEFAULT_CEILING
    (500000) is the user's explicit choice - named in oep speed <probe> 921600,500000 / --candidates, or passed by a
    caller only when its user named it. Such a rate, in the minimal and the full form alike, is committed only after its
    1 s verify in the try state: full frames (max_frame - 26) of oep.probe.link source (in) and sink (out), and duplex
    when the full form verifies it, each for at least link.FAST_VERIFY_S (1 s) at its in-flight n (the caller's, else
    the most the link keeps), judged as a flow is (broken + lost >= 3 over max(2 x baseline, 5 %)), no second run at
    n = 1 (_flow_run takes min_s). Its try asks verify_ms link.fast_verify_ms(n) (4000 for in and out, 6000 with
    duplex); a session whose lease is shorter than that + 1 s skips the candidate (trial.why says so), and
    link.lease_for(candidates, flows, verify) gives the lease to open with - open_host(port_speed=...) and oep speed
    take at least it. Committed, the rate has the probation and in-use judging of any rate. Why: on a bridge, 921600
    passed a verify of 16 full frames each way and still broke 1-4 answers in every 9 KiB upload; 500000 was clean on
    every bridge measured. Tests: test_port_speed runs its procedure tests without the ceiling (an autouse fixture) and
    the ceiling tests with it - the default (500000, no flows, verify_ms 2000), a faster rate's in / out for 1 s with
    verify_ms 4000 before the commit, a rate that breaks past the 16 frames failing its 1 s verify (500000 then taken
    with the quick verify), duplex and verify_ms 6000 in the full form, lease_for and a short lease skipping the rate,
    oep speed on a pty (500000 by default, 921600 only when named, in and out verified). linktest and tests/hw's board
    rates are unchanged (measurements at rates the caller names). README (EN / JA), oep speed's help.
  • (JA) port_speed の既定の上限は 500000(oep-spec 32260e5、oep-if-link §3 の host の義務 7、host 開発ガイド §17.3.3): 既定の
    候補は 500000 だけのまま(link.DEFAULT_CANDIDATES)で、link.DEFAULT_CEILING(500000)より速い速さは利用者が明示して選ぶもの
    • oep speed <probe> 921600,500000 / --candidates で名指すか、呼ぶ側がその利用者に名指されたときだけ渡す。その速さは、最小の
      形でも完全な形でも、試しの状態での 1 秒の確かめを通ってから決める: max_frame − 26 のフレームを oep.probe.link の source(in)と
      sink(out)で、完全な形が duplex を確かめるなら duplex でも、それぞれ link.FAST_VERIFY_S(1 秒)以上、その同時数(呼ぶ側の
      もの、無ければ link が出す最大)で流し、流し方と同じ基準(壊れ + 失われ 3 以上で max(基準 × 2, 5 %) 超)で判定し、n = 1 での
      流し直しはしない(_flow_run が min_s を取る)。その試すは verify_ms link.fast_verify_ms(n)(in と out で 4000、duplex を
      含めて 6000)を頼む。lease がそれ + 1 秒より短いセッションでは、その候補を飛ばし(trial.why に書く)、
      link.lease_for(candidates, flows, verify) が開くときの lease を出す - open_host(port_speed=...) と oep speed は少なくとも
      それで取る。決めた後は、ほかの速さと同じ試用期間と使用中の判定。理由: ある変換では 921600 が両方向 16 フレームずつの
      確かめを通っても 9 KiB の書き込みのたびに応答が 1〜4 個壊れた。500000 は測ったどの変換でも壊れなかった。試験:
      test_port_speed の手順の試験は上限なしで(autouse の fixture)、名前に ceiling を持つ試験は上限ありで回す - 既定(500000、
      流し方なし、verify_ms 2000)、速い速さの in / out 1 秒と verify_ms 4000 の後の決める、16 フレームの後で壊れる速さが 1 秒の
      確かめで落ちる(続く 500000 は素早い確かめで決まる)、完全な形での duplex と verify_ms 6000、lease_for と短い lease での
      飛ばし、pty での oep speed(既定は 500000、921600 は名指したときだけ、in と out を確かめる)。linktest と tests/hw の板の
      速さは変えない(呼ぶ側が名指す速さでの計測)。README(EN / JA)、oep speed の help。
  • (EN) The fake's esp32-v003 profile has the classic ESP32 firmware's frame limits: max_frame 512 and
    oep.target.riscv-dm max_length 488 (block_max_length(512)), as that firmware declares (oep-probe-arduino
    0.0.29-dev+526a881's describe on the V003 jig) - it had 64 and 40, and a host reproducing an upload on it split its
    block ops as no real classic ESP32 does (ch32rv's upload of a 9168-byte CH32V003 image: 244 write_blocks and 245
    read_blocks of up to 10 words, where the real probe takes 42 and 24 of up to 122). The smallest max_frame a probe may declare (64, reg.MIN_MAX_FRAME) keeps its profile:
    esp32-v003-64 (fake.esp32_v003_64(), the same probe; max_length 40, list / describe paged). fake.esp32_v003
    takes max_frame. Tests follow: paging, C-04's room for ignored, stream I/O in a 64-byte frame and port_speed's
    probation cases (a rate that breaks after 3000 bytes) on esp32-v003-64; confirm and dump read 512 on
    esp32-v003. README (EN / JA), fake_serve's help.
  • (JA) 偽物の esp32-v003 の profile は classic ESP32 の firmware と同じフレームの上限を持つ: max_frame 512 と
    oep.target.riscv-dm の max_length 488(block_max_length(512))。その firmware が宣言するとおり(oep-probe-arduino
    0.0.29-dev+526a881 の V003 治具での describe)。前は 64 と 40 で、これで書き込みを再現した host は本物の classic ESP32 では
    ありえない分け方で block op を出した(ch32rv による 9168 バイトの CH32V003 のイメージの書き込み: 10 語までの write_block 244 個と
    read_block 245 個。本物の probe では 122 語までの 42 個と 24 個)。probe が宣言できる最小の max_frame(64、reg.MIN_MAX_FRAME)は別の profile に残す: esp32-v003-64
    (fake.esp32_v003_64()、同じ probe。max_length 40、list / describe は分けて読む)。fake.esp32_v003 は max_frame を取る。
    試験もそれに合わせる: 分けて読む list / describe、C-04 の ignored の余地、64 バイトのフレームでのストリームの入出力、port_speed の
    probation(3000 バイトの後で壊れる速さ)は esp32-v003-64 で、confirm と dump は esp32-v003 で 512 を読む。README(EN / JA)、
    fake_serve の help。
  • (EN) riscv-dm read_register / write_register take cmderr 6 for a failed try, not an error: QingKe's "parity bit error"
    • the module took one of the group's frames for a bad one and ignored it, a missed access with the link up again at
      once (bench, oep-probe-arduino 0.0.29-dev+f594f04, tests/hw test_wire on a CH32L103 through an RVSWD probe:
      "read_register 0x100a / 0x1009 / 0x100b failed (cmderr 6)", 3 of 8 runs; a probe's own console write can set it
      too). The group (which clears cmderr first) is run again within the held tries (RiscvDm._held_abstract; read twice
      over the sentinels as before); still cmderr 6 after every try: cmderr cleared, LinkNotHeld. Any other cmderr is
      cleared and raised (RuntimeError) as before. riscv.CMDERR_PARITY. The fake: FakeTarget.drop_at mode
      "glitch_parity" (a write missed with cmderr 6 set). Tests (test_link_drop): a glitch_parity at every access of
      read_register and write_register - the right value, no cmderr left, never raised (before: RuntimeError); two at every
      pair of read_register's accesses - never a wrong value nor an error; cmderr 6 at every try - LinkNotHeld, cmderr
      cleared. tests/hw test_wire: when an op raises, the record keeps the loop, the error, DMSTATUS read twice and DMCONTROL
      (raw dmi) and dpc when halted (printed too). README (EN / JA).
  • (JA) riscv-dm の read_register / write_register は cmderr 6 を誤りでなく失敗した試みとして扱う: QingKe の「parity bit error」
    • module がまとまりの frame の 1 つを悪いものと取って無視した、すぐにまた繋がる link の 1 回の取りこぼし(bench、
      oep-probe-arduino 0.0.29-dev+f594f04、RVSWD の probe の先の CH32L103 での tests/hw test_wire: 「read_register 0x100a / 0x1009 /
      0x100b failed (cmderr 6)」が 8 回中 3 回。probe 自身のコンソールの書き込みでも立ちうる)。まとまり(最初に cmderr を消す)を
      held の試みの回数のうちでやり直す(RiscvDm._held_abstract。今までどおり 2 つの sentinel で 2 回読む)。どの試みでも cmderr 6
      なら cmderr を消して LinkNotHeld。ほかの cmderr は今までどおり消してから RuntimeError。riscv.CMDERR_PARITY。偽物:
      FakeTarget.drop_at の "glitch_parity"(書き込みを取りこぼし cmderr 6 を立てる)。試験(test_link_drop): read_register と
      write_register のすべてのアクセスに glitch_parity - 正しい値、cmderr は残らず、例外にならない(前: RuntimeError)。read_register
      のアクセスのすべての組に 2 つ - 誤った値も例外も無い。どの試みでも cmderr 6 - LinkNotHeld で cmderr は消える。tests/hw
      test_wire: op が例外を出したとき、記録にその回、例外、2 回読んだ DMSTATUS と DMCONTROL(生の dmi)、止まっていれば dpc を残す
      (出力にも出す)。README(EN / JA)。
  • (EN) tests/hw: the config test's reboot through oep.probe.restart is opt-in on a USB probe (the P4s, the RP2s:
    Board.usb): only with OEP_HW_RESTART=1; without it the reboot is skipped and the record says why ("oep.probe.restart
    on a USB probe needs OEP_HW_RESTART=1 ..."), the rest of the config test runs (bench: with oep-probe-arduino
    0.0.29-dev+3c0cd99 an RP2350 and an ESP32-P4 came back from a restart failing enumeration until a replug). A classic
    ESP32 behind a bridge still reboots through EN; the fake through its oep.probe.restart. README (EN / JA).
  • (JA) tests/hw: config の試験の oep.probe.restart による再起動は、USB の probe(P4、RP2: Board.usb)では選んだときだけ:
    OEP_HW_RESTART=1 のときだけ行い、無ければ再起動を飛ばして記録に理由を書き("oep.probe.restart on a USB probe needs
    OEP_HW_RESTART=1 ...")、config の試験の残りは行う(bench: oep-probe-arduino 0.0.29-dev+3c0cd99 では RP2350 と ESP32-P4 が
    再起動の後に列挙に失敗し、抜き差しが要った)。bridge の classic ESP32 は今までどおり EN で、偽の probe はその oep.probe.restart
    で再起動する。README(EN / JA)。
  • (EN) tests/hw: test_wire prints the whole change record when a register changes over a read_block - every change's before, after, the read again, the two words past the block and dpc, one JSON line each, in the failure message (pytest.fail, no repr cut) and on stdout - instead of an assert whose repr pytest cut. Each run keeps its own results file: tests/hw/results/---.json (the run's start, e.g. 20261006T203121), so a later run no longer overwrites an earlier one; README (EN / JA) says so
  • (JA) tests/hw: test_wire は read_block の間にレジスタが変わったとき、変化の記録をすべて出す - 変化ごとの前、後、読み直し、block の直後の 2 語、dpc を 1 行 1 つの JSON で、失敗のメッセージ(pytest.fail。repr で切られない)と stdout に。以前は pytest が repr を切る assert だった。巡ごとに結果のファイルを別に残す: tests/hw/results/---.json(巡の開始時刻、例 20261006T203121)。後の巡が前の巡のものを上書きしない。README(EN / JA)もそう書く
  • (EN) Breaking (wire and API): oep-spec 289bde0 .. 498ae95, the 2026-10-06 structure. README names 498ae95. Registry and vectors synced (tools/sync_registry.sh; ops_encoding.json new).
    • The core has no name (core §0, §7.2): fn 0 is never a list entry (core.list_entries leaves out an fn 0 a probe lists anyway); registry.CORE has no name and INTERFACES["oep.core"] is gone. fn 0's ops are the eight mandatory ones: confirm, list, describe, clock, open, end, keepalive, lock_state (message.CORE_OPS). Removed: message.OP_PLAN_APPLY / OP_PLAN_RELEASE / OP_RESTART, host.Host.heartbeat_seen / uptime_ns, the link's on_heartbeat / heartbeats.
    • fn 0's clock (core §7.7, 0x04: lock-free, session-free; answer boot_id(u32) uptime_ns(u64)): Host.clock(now=time.monotonic_ns) -> host.ClockReading(before_ns, after_ns, uptime_ns, boot_id, round_trip_ns) (.host_ns the midpoint, .uncertainty_ns half the round trip, host guide §12), Host.clock_best(n=8) keeps the shortest round trip; the boot_id is watched like confirm's. CLI oep clock <probe> [-n N] [--json].
    • oep.probe.plan (oep-if-plan: plan_apply 0x01, plan_release 0x02, describe 0x40 plan_roles): core.plan_apply / plan_release find it by name (core.plan_fn, core.PLAN_NAME, core.OP_PLAN_APPLY / OP_PLAN_RELEASE / TAG_PLAN_ROLES, core.plan_roles(hst)); fn 0's describe no longer has plan_roles.
    • oep.probe.restart (oep-if-restart: restart 0x01, describe 0x40 restart_max_ms): Host.restart_probe() / request_restart() find it by name (LookupError and nothing sent when the probe lists none; core.restart_fn, core.RESTART_NAME, core.OP_RESTART, host.OP_RESTART); core.restart_max_ms reads its describe; the link takes a completed restart on that fn (SerialLink.restart_fn) back to the boot speed. CLI oep restart <probe>.
    • oep.link is oep.probe.link (registry.PROBE_LINK; core.link_fn and the rest unchanged).
    • Notifications (core §11.3): subscribe / unsubscribe are ops 0x30 / 0x32 of the emitting interface, with no target fn (Host.subscribe(fn, min_bytes, max_delay_ms) sends min_bytes(u16) max_delay_ms(u32) to that fn, unsubscribe(fn) nothing; the blind stops too); message.OP_SUBSCRIBE / OP_UNSUBSCRIBE are registry.OP_SUBSCRIBE / OP_UNSUBSCRIBE. No heartbeat; the captures' features bit2 notify is gone.
    • The ops encoding (core §7.4): catalog.check_ops(value) (why it breaks: 2-33 bytes, base + 8 x bitmap <= 256, bit 0 set, last byte not 0), catalog.InvalidOps from unpack_ops, pack_ops of an empty set raises, Description.ops_invalid. A host does not use an fn whose ops break it (core.ops and Interface() raise core.UnusableFunction) and does not use a probe whose fn 0 ops do (core.describe -> NotUsable).
    • dump: fn 0 is described first as (core) (its ops named), then the listed interfaces (the text counts those); MISSING names fn 0's ops lacking a core op or breaking the encoding, an fn whose ops break it (shown UNUSABLE), a list entry with fn 0, and oep.probe.restart's restart_max_ms; restart max ms and plan roles are named under their interfaces. names.kind says oep instead of standard (no standard-interface category, core §13).
    • The fake: no fn 0 in list; fn 0's ops exactly the eight; every profile lists oep.probe.plan and oep.probe.restart after its existing fns (p4-x035 14 / 15, esp32-v003 11 / 12, p4-bench 8 / 9, rp2350-pins 7 / 8; no earlier fn moved; fake._plan, fake._restart, fake.PLAN / RESTART / LINK, PLAN_ROLES_TAG, RESTART_MAX_MS_TAG), a probe with plan roles and no plan gets one at the next fn, fake.without(probe, name); fn 0's clock; logic / analog / capture-group set subscribe / unsubscribe in their ops (others: unknown_operation), Endpoint.subscribed is fn -> (min_bytes, max_delay_ms) and min_bytes / max_delay_ms batch a streaming capture's data only, events go at once; no heartbeat; fake_serve --no-restart lists no oep.probe.restart (Endpoint.restart_offered removed; Endpoint.restart_fn); a test's broken ops value is served as given. Removed: fake.CORE_OPTIONAL, CORE_PLAN_ROLES, CORE_RESTART_MAX_MS.
    • tests/hw: identity checks that no list entry is fn 0 and records clock (boot_id as confirm's, the shortest of 4 round trips); config's reboot step uses oep.probe.restart when the probe lists it (else the EN line, else skipped); probe_info records plan_fn / restart_fn / restart_max_ms.
  • (JA) 破壊的(wire と API): oep-spec 289bde0〜498ae95、2026-10-06 の構成。 README は 498ae95 を名乗る。registry とベクタを同期(tools/sync_registry.sh。ops_encoding.json は新しい)。
    • 本体は名前を持たない(core §0、§7.2): fn 0 は list の項目にならない(probe が fn 0 を並べても core.list_entries は捨てる)。registry.CORE は名前を持たず、INTERFACES["oep.core"] は無くなった。fn 0 の ops は必須の 8 つ: confirm、list、describe、clock、open、end、keepalive、lock_state(message.CORE_OPS)。削除: message.OP_PLAN_APPLY / OP_PLAN_RELEASE / OP_RESTART、host.Host.heartbeat_seen / uptime_ns、link の on_heartbeat / heartbeats。
    • fn 0 の clock(core §7.7、0x04: ロックもセッションも要らない。応答 boot_id(u32) uptime_ns(u64)): Host.clock(now=time.monotonic_ns) -> host.ClockReading(before_ns, after_ns, uptime_ns, boot_id, round_trip_ns)(.host_ns は中点、.uncertainty_ns は往復の半分、host ガイド §12)。Host.clock_best(n=8) は往復が最短のものを残す。boot_id は confirm のものと同じに見張る。CLI oep clock <probe> [-n N] [--json]。
    • oep.probe.plan(oep-if-plan: plan_apply 0x01、plan_release 0x02、describe 0x40 plan_roles): core.plan_apply / plan_release は名前で探す(core.plan_fn、core.PLAN_NAME、core.OP_PLAN_APPLY / OP_PLAN_RELEASE / TAG_PLAN_ROLES、core.plan_roles(hst))。fn 0 の describe に plan_roles はもう無い。
    • oep.probe.restart(oep-if-restart: restart 0x01、describe 0x40 restart_max_ms): Host.restart_probe() / request_restart() は名前で探す(probe が出していなければ LookupError で、何も送らない。core.restart_fn、core.RESTART_NAME、core.OP_RESTART、host.OP_RESTART)。core.restart_max_ms はその describe を読む。link はその fn の completed の restart で起動時の速さに戻す(SerialLink.restart_fn)。CLI oep restart <probe>。
    • oep.link は oep.probe.link(registry.PROBE_LINK。core.link_fn ほかは変わらない)。
    • 通知(core §11.3): subscribe / unsubscribe は通知を送り出すインターフェースの op 0x30 / 0x32 で、相手の fn を持たない(Host.subscribe(fn, min_bytes, max_delay_ms) はその fn に min_bytes(u16) max_delay_ms(u32) を、unsubscribe(fn) は何も無しで送る。blind の止めも同じ)。message.OP_SUBSCRIBE / OP_UNSUBSCRIBE は registry.OP_SUBSCRIBE / OP_UNSUBSCRIBE。heartbeat は無い。キャプチャの features bit2 notify も無い。
    • ops の符号(core §7.4): catalog.check_ops(value)(破る理由: 2〜33 byte、base + 8 × bitmap ≤ 256、bit 0 が立つ、最後の byte が 0 でない)、unpack_ops は catalog.InvalidOps を上げる、空の集合の pack_ops は例外、Description.ops_invalid。host は ops が符号を破る fn を使わず(core.ops と Interface() は core.UnusableFunction)、fn 0 の ops が破る probe を使わない(core.describe -> NotUsable)。
    • dump: 先に fn 0 を (core) として describe し(ops を名前で)、続けて list のインターフェース(文の数はそれを数える)。MISSING は、本体の op が欠けるか符号を破る fn 0 の ops、符号を破る fn の ops(UNUSABLE と出す)、fn 0 の list の項目、oep.probe.restart の restart_max_ms を挙げる。restart max ms と plan roles はそのインターフェースの下に名前で出す。names.kind は standard の代わりに oep を返す(標準インターフェースという区分は無い、core §13)。
    • 偽の probe: list に fn 0 は無い。fn 0 の ops はちょうど 8 つ。どの profile も前からの fn の後ろに oep.probe.plan と oep.probe.restart を並べる(p4-x035 は 14 / 15、esp32-v003 は 11 / 12、p4-bench は 8 / 9、rp2350-pins は 7 / 8。前からの fn は動かない。fake._plan、fake._restart、fake.PLAN / RESTART / LINK、PLAN_ROLES_TAG、RESTART_MAX_MS_TAG)。plan の role があって plan の無い probe は次の fn に plan を得る。fake.without(probe, name)。fn 0 の clock。logic / analog / capture-group は ops に subscribe / unsubscribe を立てる(ほかは unknown_operation)。Endpoint.subscribed は fn -> (min_bytes, max_delay_ms) で、min_bytes / max_delay_ms はストリーミングのキャプチャのデータだけをまとめ、出来事はすぐ送る。heartbeat は無い。fake_serve --no-restart は oep.probe.restart を並べない(Endpoint.restart_offered は削除、Endpoint.restart_fn)。試験が与えた壊れた ops の値はそのまま出す。削除: fake.CORE_OPTIONAL、CORE_PLAN_ROLES、CORE_RESTART_MAX_MS。
    • tests/hw: identity は fn 0 の list の項目が無いことを確かめ、clock を記録する(boot_id が confirm と同じ、4 回の往復の最短)。config の再起動は、probe が oep.probe.restart を出していればそれを使う(無ければ EN の線、それも無ければ飛ばす)。probe_info は plan_fn / restart_fn / restart_max_ms を記録する。
  • (EN) riscv-dm read_register reads the register twice in its held group and takes it only when both agree: DATA0 set to 0 before the first access-register command and to all ones before the second (riscv.READ_SENTINELS), DMSTATUS read before the second DATA0 read. Bench (oep-probe-arduino 0.0.29-dev+bd19b00, tests/hw test_wire on a CH32L103 through an RVSWD probe): 5 of 8 runs read s1 or a0 wrong after a read_block with the group's looks passing (a0 read as s1's value: the command lost, DATA0 still the read before). A lost command leaves its sentinel and a DATA0 read that gives the value read before it gives ABSTRACTCS the first time and DMSTATUS the second, so one missed access cannot make the two agree on anything but the register's value. RiscvDm.held takes agree (a try whose values it refuses is run again, as one whose looks fail) and its docs say what the looks cover: a probe that brings the link up again between them is one with the per-request check (oep-probe-arduino 4c310a2: such a request answers line, no values). The fake: FakeTarget.drop_at mode "glitch" (that one access missed, the link up again at once). tests/test_link_drop.py: a glitch at every access - af3789a's one-command group returned DATA0's old word, the read twice never a wrong value, also with two glitches at every pair of accesses. tests/hw test_wire: on a change the registers are read again and the record keeps them, the two words past the block (what the probe's reader loads last) and dpc - a value that stays says the target changed, one back as before that a read was wrong.
  • (JA) riscv-dm の read_register は、保ったまとまりの中でレジスタを 2 回読み、両方が一致したときだけ採る: 1 回目の access-register の command の前に DATA0 を 0、2 回目の前に全 1 にし(riscv.READ_SENTINELS)、2 回目の DATA0 の読みの前に DMSTATUS を読む。bench(oep-probe-arduino 0.0.29-dev+bd19b00、RVSWD の probe 越しの CH32L103 で tests/hw test_wire): 8 回中 5 回、read_block の後に s1 か a0 を誤って読んだ。まとまりの見張りは通っていた(a0 を s1 の値と読んだ: command が消え、DATA0 は前に読んだ値のまま)。消えた command は番兵を残し、前の読みの値を返す DATA0 の読みは 1 回目は ABSTRACTCS、2 回目は DMSTATUS を返すので、抜けたアクセスが 1 つならレジスタの値以外で一致することはない。RiscvDm.held は agree を取り(値を断られた試しは、見張りが通らなかったときと同じくやり直す)、見張りが何を保証するかを docs に書いた: 見張りの間でリンクを戻す probe は要求ごとの確かめを持つもの(oep-probe-arduino 4c310a2: その要求は line、値なしで答える)。fake: FakeTarget.drop_at の mode "glitch"(その 1 アクセスだけ抜け、すぐ戻る)。tests/test_link_drop.py: 各アクセスに glitch - af3789a の command 1 回のまとまりは DATA0 の古い語を返し、2 回読みは誤った値を返さない(2 つのアクセスの全組に glitch を入れても)。tests/hw test_wire: 変化があったらレジスタをもう一度読み、それとブロックの直後の 2 語(probe の読み手が最後に読むところ)と dpc を記録に残す - 残る値は target が変わったこと、元に戻る値は読みが誤ったことを示す。
  • (EN) Raw DMI groups over a debug link that drops (oep-probe-arduino ec57b23 / f594f04: a CH32L103 behind an RVSWD probe drops its link for about 0.7 - 2 ms after a change of hart state; writes are lost and reads give the last value read or all ones, and nothing in the answer says so). RiscvDm.read_register took such a read as the register (a lost command left DATA0's old word; tests/hw test_wire's before / after could report a clobber that was not there, or miss one). New RiscvDm.held(steps, what, tries=HELD_TRIES): one dmi request of look + steps + look, a look being DMSTATUS (a found version, authenticated) and DMCONTROL (dmactive, hart 0) - riscv.link_held; a dropped link reads one value for both and cannot pass. A try whose looks fail, or that stopped line / timeout / wait, is run again after HELD_RETRY_S (5 ms), 4 tries at most, then riscv.LinkNotHeld; nothing unconfirmed is returned. read_register and the new write_register are held groups that clear cmderr first (so a redo, or a cmderr left behind, does not have the command ignored); riscv.DATA0 / DMCONTROL / DMSTATUS / ABSTRACTCS / COMMAND. uiapduino.run_payload writes mstatus / dpc with write_register and reads them back (MIE clear, dpc the payload) before the resume writes, which stay unheld (a change of hart state; uiapduino._write_register, ABSTRACTCS, COMMAND, DATA0 removed). ch32_flash.resume and tests/hw test_wire get it through read_register. Not affected: the probe's block ops, run, step, halt, resume and reset (the probe holds them since ec57b23), targets / ch32_flash.program (block ops and run), arm (SWD transfers have an ACK). The fake: FakeTarget reads DMSTATUS / DMCONTROL as the module has them, runs access-register commands on regs / dpc (cmderr ignored-while-set, 4 on a running hart, W1C), haltreq / resumereq move the hart, and drops the link at a chosen access (drop_at: "stale", "ones", "ones_line"; drop_requests, dmi_accesses, lost_writes). tests/test_link_drop.py: every access of read_register, the payload's register writes and ch32_flash.resume dropped in turn in each mode - the old code returned DATA0's old word or all ones, left the hart off the payload, or said a resume went; the held groups return the right value (drop over by the next request) or raise LinkNotHeld (a drop that lasts).
  • (JA) 落ちるデバッグの link の上の生の DMI のまとまり(oep-probe-arduino ec57b23 / f594f04: RVSWD の probe の先の CH32L103 は hart の状態が変わった後 0.7〜2 ms ほど link を落とす。その間 write は失われ、read は最後に読んだ値かすべて 1 を返し、応答にはそれを示すものが無い)。RiscvDm.read_register はそういう read をレジスタの値としていた(失われた command で DATA0 の古い語が残る。tests/hw の test_wire の前後比較は無い書き換えを報告したり、本物を見逃したりし得た)。新しい RiscvDm.held(steps, what, tries=HELD_TRIES): look + steps + look を 1 つの dmi 要求で送る。look は DMSTATUS(scan が見つける version、authenticated)と DMCONTROL(dmactive、hart 0)- riscv.link_held。落ちた link は両方に同じ値を返すので通らない。look が通らない試み、line / timeout / wait で止まった試みは HELD_RETRY_S(5 ms)おいてやり直し、4 回まで、その後は riscv.LinkNotHeld。確かめられない値は返さない。read_register と新しい write_register は cmderr を先に消す held のまとまり(やり直しや残った cmderr で command が無視されない)。riscv.DATA0 / DMCONTROL / DMSTATUS / ABSTRACTCS / COMMAND。uiapduino.run_payload は mstatus / dpc を write_register で書いて読み戻し(MIE が 0、dpc が payload)、その後の resume の write は held にしない(hart の状態の変化。uiapduino._write_register、ABSTRACTCS、COMMAND、DATA0 は削除)。ch32_flash.resume と tests/hw の test_wire は read_register を通して直る。影響なし: probe の block の op、run、step、halt、resume、reset(ec57b23 から probe が held にする)、targets / ch32_flash.program(block の op と run)、arm(SWD の転送には ACK がある)。偽の probe: FakeTarget は DMSTATUS / DMCONTROL をモジュールの値で読み、access register の command を regs / dpc に対して走らせ(cmderr が立っていれば無視、走る hart には 4、W1C)、haltreq / resumereq で hart を動かし、選んだアクセスで link を落とす(drop_at: "stale"、"ones"、"ones_line"。drop_requests、dmi_accesses、lost_writes)。tests/test_link_drop.py: read_register、payload のレジスタの write、ch32_flash.resume のアクセスを 1 つずつ各モードで落とす。旧コードは DATA0 の古い語やすべて 1 を返し、hart を payload から外したまま、または resume が通ったと言った。held のまとまりは正しい値を返す(次の要求で落ちが終わる)か LinkNotHeld を上げる(落ちが続く)。
  • (EN) tests/hw on the new wire: port_speed is read from oep.link's ops (oep-if-link §1), no longer from fn 0's describe (the run raised KeyError 'port_speed' in flash / identity); the record keeps link_fn and port_speed only when the probe offers them. The V003 jig's ESP32 wires every output-capable channel but SWIO / NRST to the V003, so its table entry gives only the input-only 34 / 35 (config's disable / label, the analog capture): gpio / capture / capture_group skip there, uart runs on the jig's settings plan or skips, i2c / spi target find too few channels and skip. A table entry's 0 channels make a fixture test skip; Board.label names config's label channel. The X035 P4 and RP2350 entries were checked against their bench wiring: unchanged.
  • (JA) 新しい wire での tests/hw: port_speed は fn 0 の describe でなく oep.link の ops(oep-if-link §1)から読む(flash / identity が KeyError 'port_speed' を出していた)。記録の link_fn と port_speed は probe が出すときだけ。V003 のジグの ESP32 は SWIO / NRST 以外の出力できるチャネルをすべて V003 につないでいるので、表が与えるのは入力専用の 34 / 35(config の disable / label、アナログのキャプチャ)だけ: そこでは gpio / capture / capture_group は skip、uart はジグの設定の plan で走るか skip、i2c / spi target はチャネルが足りず skip。表のチャネルが 0 なら fixture の試験は skip する。Board.label が config の label のチャネル。X035 の P4 と RP2350 の項目は bench の結線と突き合わせた: 変更なし。
  • (EN) oep-spec 3c96daf (core §1.2, §6.6, §7.5; transports §1): fn 0 describe restart_max_ms (tag 0x4F, u32, required with restart): the longest from restart's answer until the probe answers confirm again on the same transport. Registry and vectors synced (ops.json's restart states name it). README adds 3c96daf beside ecd1ab9. The fake declares restart_max_ms 2000 with restart (fake.RESTART_MAX_MS, fake.CORE_RESTART_MAX_MS; FakeProbe adds it to fn 0 whose ops set restart; Endpoint.restart_max_ms; describe carries it exactly while restart_offered, so --no-restart drops both). Host.restart_probe(wait_s=None) waits the probe's restart_max_ms (read before the restart, core.restart_max_ms), or RESTART_WAIT_S (10 s) when it declares none; past it the probe is gone (ConnectionError / TimeoutError). dump names restart_max_ms missing when restart is in ops. Tests: tests/test_restart.py (the describe value, the default wait, giving up after it), tests/test_capabilities.py.
  • (JA) oep-spec 3c96daf(core §1.2、§6.6、§7.5。transports §1): fn 0 の describe の restart_max_ms(tag 0x4F、u32、restart を持つなら必須): restart の応答から同じ経路で confirm にまた答えるまでの最長の時間。registry とベクタを同期(ops.json の restart の状態がそれを書く)。README は ecd1ab9 に並べて 3c96daf を足す。偽の probe は restart とともに restart_max_ms 2000 を出す(fake.RESTART_MAX_MS、fake.CORE_RESTART_MAX_MS。FakeProbe は ops に restart のある fn 0 に足す。Endpoint.restart_max_ms。describe は restart_offered の間だけそれを持つので、--no-restart で両方が外れる)。Host.restart_probe(wait_s=None) は probe の restart_max_ms(restart の前に読む、core.restart_max_ms)まで、宣言が無ければ RESTART_WAIT_S(10 s)まで待ち、過ぎれば probe は無くなったものとする(ConnectionError / TimeoutError)。dump は ops に restart があって restart_max_ms が無ければ欠けていると示す。試験: tests/test_restart.py(describe の値、既定の待ち、過ぎたらあきらめる)、tests/test_capabilities.py。
  • (EN) oep-spec ecd1ab9 (core §6.6, §12; link §3 host obligation 6): fn 0's optional restart (op 0x14, needs the lock, limits.restart_after_answer_ms 100). Registry and vectors synced (ops.json: the answer, session_required, no_session, unknown_operation). README names 59dd028 with the restart from ecd1ab9. The fake: every profile offers it (fake.default_ops for fn 0 adds fake.CORE_OPTIONAL; fake.CORE_REQUIRED is public; Endpoint.restart_offered, fake_serve --no-restart); it answers completed success with no payload and, once the answer is out (Endpoint.after_answer, which also does what speed_after_answer did), reboots (Endpoint.reboots counts); in between nothing is processed or pushed; a serial port keeps the queued answer and drops the bytes it read behind the request, fake_serve's TCP length framing drops its buffer; the pty or TCP connection stays. The client: Host.request_restart() (the request alone; the session and the fn names are gone after it), Host.restart_probe(wait_s=10) (restart, then SerialLink.reopen_after_restart: close, wait, open again as a new open - confirm first, the boot speed, USB found again, TCP reconnected; or, on a bare send, wait and confirm - and return the new boot_id; host.NotRestarted when it stayed the same; a lost answer refused no_session on its resend counts as done), message.OP_RESTART, host.RESTART_AFTER_ANSWER_S; a completed restart puts a raised serial link back at its boot speed like end. SerialLink.reopener (open_host sets it for a serial path, TCP and USB). tests/test_restart.py.
  • (JA) oep-spec ecd1ab9(core §6.6、§12。link §3 の host の義務 6): fn 0 の任意の restart(op 0x14、ロックが要る、limits.restart_after_answer_ms 100)。registry とベクタを同期(ops.json: 応答、session_required、no_session、unknown_operation)。README は 59dd028 と ecd1ab9 の restart を名乗る。偽の probe: どの profile も持つ(fn 0 の fake.default_ops は fake.CORE_OPTIONAL を足す。fake.CORE_REQUIRED を公開。Endpoint.restart_offered、fake_serve の --no-restart)。payload の無い completed success で答え、応答が出たら(Endpoint.after_answer。speed_after_answer の役目も持つ)再起動する(Endpoint.reboots が数える)。その間は何も処理せず、何も送らない。シリアルの口は積んだ応答を残し、要求の後ろに読んだバイトを捨てる。fake_serve の TCP の length の形はバッファを捨てる。pty と TCP の接続は残る。クライアント: Host.request_restart()(要求だけ。後にセッションと fn の名前は残らない)、Host.restart_probe(wait_s=10)(restart の後、SerialLink.reopen_after_restart で閉じ、待ち、新しく開くのと同じに開き直す。最初は confirm、起動時の速さ、USB は探し直し、TCP は接続し直す。素の send なら待って confirm する。新しい boot_id を返し、同じなら host.NotRestarted。応答が失われ送り直しが no_session で断られたときも済んだとみなす)、message.OP_RESTART、host.RESTART_AFTER_ANSWER_S。completed の restart は end と同じく、上げたシリアルの link を起動時の速さに戻す。SerialLink.reopener(open_host がシリアルの口、TCP、USB で設定する)。tests/test_restart.py。
  • (EN) oep-spec 59dd028 (debug §1, §3; probe-config §1.1): an attach that joins an existing connection keeps the connection's setting for every setting TLV it does not carry - idle_clock absent keeps the current rest (absent = high is for a new connection only), max_speed is always carried and only lowers the speed - and a scan never changes a live connection's settings. README names 59dd028; the registry and vectors did not change since 4bd3a87. The fake already behaved so; its docstrings say it and tests/test_spec_59dd028.py checks it (a join without idle_clock keeps low, one with high changes it, a slot's low rest survives a host join, a new connection rests high, a scan over a live pair leaves its speed and rest). The client: Wire.attach sends idle_clock whenever it is given, high included, and its docstring says a caller that knows the target's rest (a slot's idle_clock) passes it; no client helper attaches to a slot with the slot's settings (the pin finder attaches to pins it found, before it knows the family; uiapduino targets swio, which has no idle_clock).
  • (JA) oep-spec 59dd028(debug §1、§3。probe-config §1.1): 既存の connection に加わる attach は、運ばない設定の TLV について connection の今の設定を保つ。idle_clock が無ければ今の休ませ方のまま(無いとき high は新しい connection だけ)、max_speed はいつも運ばれ速さを下げるだけ。scan は生きている connection の設定を変えない。README は 59dd028 を名乗る。registry とベクタは 4bd3a87 から変わっていない。偽の probe はすでにそう動いていた。docstring に書き、tests/test_spec_59dd028.py で確かめる(idle_clock の無い join は low を保ち、high を運べば替わり、スロットの low は host の join をまたいで残り、新しい connection は high で休み、生きている組の scan は速さと休ませ方を変えない)。クライアント: Wire.attach は idle_clock を与えられればいつも送り(high も)、docstring に、target の休ませ方(スロットの idle_clock)を知る呼び手はそれを渡すと書いた。スロットの設定でスロットに attach するクライアントの helper は無い(ピン探しは見つけたピンに family を知る前に attach する。uiapduino は idle_clock の無い swio)。
  • (EN) Breaking: oep-spec 4621eab .. 4bd3a87. README names 4bd3a87. Registry and vectors synced again (ops.json: console cases on stream 2, link source past max_frame - 26; discovery.json: fn 0 sets every required op).
    • Console send queue (f0c68bf, R-A; console §1, §2, §3, common §1.4): a write goes into the stream's send queue, accepted = min(count, its free space), 0 (completed failed) only when it is full; SDI takes nothing. Console.send_queue reads the describe's tag 0x41 (u16, at least console_send_queue_min_bytes 64), Console.mechanisms(); ConsoleIO writes a send_queue at a time (ConsoleIO.send_queue) and goes on from each accepted. The fake: every console with DMDATA or dmseq declares send_queue 256 (fake._console, fake.CONSOLE_SEND_QUEUE); each stream's queue (Stream.queue; Endpoint.send_queue) is handed to the target 3 (DMDATA) / 2 (dmseq) bytes a poll, one poll per ms while the hart runs and answers, console_take hands all of it; it stays over resets and restarts, clear leaves it, it goes when the stream closes (console_slot, Stream.slot removed).
    • Reset settle wait (f0c68bf, R-B; debug §3, §4.3, core §4.4): Wire.attach_ms(reset) is attach_budget_ms + hold_ms + reset_settle_ms (700) with the reset TLV, RiscvDm.reset_ms() reset_settle_ms (both at most max_op_ms) - what the host's wait counts as argument time. The fake: FakeTarget.restart_ms (the DM silent after a reset's release); riscv-dm reset and attach with the reset TLV wait for it up to reset_settle_ms, then answer status line (reset: no flags bit2, the connection kept; attach: an existing connection kept and marked reset detail 3); Endpoint.settle_log.
    • d34dafa: a probe.config item longer than its definition is a longer request TLV (core §2.3) - unsupported when critical, else not applied and listed in ignored, the other items applied; a label's text runs to the end (the fake already did; tests added). 4bd3a87: oep.link source's len is at most max_frame - 26 (link_source_overhead_bytes), the ignored room kept always; core.link_size(max_frame); raise_speed's flows, linktest's default size and core.link_speed use it (was max_frame - 16).
  • (JA) 破壊的: oep-spec 4621eab〜4bd3a87。 README は 4bd3a87 を名乗る。registry とベクタを同期し直した(ops.json: コンソールの場合はストリーム 2、max_frame − 26 を超える link の source。discovery.json: fn 0 は必須の op をすべて立てる)。
    • コンソールの送りの列(f0c68bf、R-A。console §1、§2、§3、common §1.4): write はストリームの送りの列に入り、accepted = count と列の空きの小さい方、0(completed failed)は列が満ちたときだけ。SDI は受けない。Console.send_queue は describe の tag 0x41(u16、console_send_queue_min_bytes 64 以上)を読む。Console.mechanisms()。ConsoleIO は send_queue ずつ書き(ConsoleIO.send_queue)、accepted の続きから送る。偽の probe: DMDATA か dmseq を持つコンソールは send_queue 256 を宣言する(fake._console、fake.CONSOLE_SEND_QUEUE)。ストリームごとの列(Stream.queue、Endpoint.send_queue)を poll ごとに DMDATA 3、dmseq 2 byte ずつ target に渡す(hart が走って答える間、1 ms に 1 回。console_take は全部渡す)。列は reset と再起動をまたいで残り、clear は触れず、ストリームが閉じると消える(console_slot、Stream.slot は削除)。
    • reset の後の待ち(f0c68bf、R-B。debug §3、§4.3、core §4.4): Wire.attach_ms(reset) は reset TLV があれば attach_budget_ms + hold_ms + reset_settle_ms(700)、RiscvDm.reset_ms() は reset_settle_ms(どちらも max_op_ms まで)。host の待ちが引数の時間として数えるもの。偽の probe: FakeTarget.restart_ms(reset を離した後 DM が黙る時間)。riscv-dm の reset と reset TLV つきの attach はそれを reset_settle_ms まで待ち、過ぎれば status line で答える(reset は flags bit2 を立てず connection を保つ。attach は既存の connection を保ち mark reset detail 3)。Endpoint.settle_log。
    • d34dafa: 定義より長い probe.config の項目は長い要求の TLV(core §2.3): critical なら unsupported、そうでなければ適用せず ignored に載せ、ほかの項目は適用する。label の text は値の終わりまで(偽の probe はすでにそうしていた。試験を足した)。4bd3a87: oep.link の source の len は max_frame − 26(link_source_overhead_bytes)まで、ignored の場所はいつも残す。core.link_size(max_frame)。raise_speed の流し方、linktest の既定の大きさ、core.link_speed はそれを使う(以前は max_frame − 16)。
  • (EN) Breaking (wire and API): oep-spec b69ec26 .. 4621eab, the 2026-10-06 simplification (docs/v1-simplification-proposal-2026-10-06.ja.md, every item applied; until the freeze the Japanese text is the working text). The README names the spec commit this implements (4621eab; no v0.x tag yet, versioning §6). Registry and vectors synced (sessions.json and ops.json new).
    • Codec (core §2.2, §2.3, §4.1): one 10-byte request header role 0x01, corr(u16), fn(u16), op(u8), session_id(u32) on every request, 0 = no session (role 0x81 is gone; message.Request.session is an int, None taken as 0; REQUEST_HEADER 10, ROLE_SESSION / SESSION_BYTES removed). One TLV form tag(u8) len(u16) value (TLV_LEN_LONG, TLV_SHORT_MAX, BadTlv removed). Sequences are count x element with no element length (list, connections, scan, marks, streams, segments, probe.config state; Reader.element and message.element removed). Fixed forms are closed: probe.config's idle is 6 bytes (drive_kind 2 = the default level, value 0; an input idle carries kind 2), a slot carries boot_reset right after attach and ends with the lock, slot_state carries reset_at_ns before the tid, a bind's streams are 3 bytes each; fixture.Drive.default().
    • ops (core §1.2, §7.4): every fn's describe carries the ops tag (0x09, base + bitmap), the one declaration of the ops an fn offers; it replaces the features bits that declared optional ops (riscv-dm block / run / reset / step, i2c-target stretch, logic / analog query and force, capture-group force) and fn 0's describe 0x4E. New: core.ops(hst, fn), core.offers(hst, fn, op), Interface.ops() / .offers(op), core.require / core.not_offered (raise, without sending, the same Rejected with detail unknown_operation the probe answers; the clients themselves still send and let the probe answer); catalog.pack_ops / ops_tlv / unpack_ops, Description.ops; RiscvDm.declared() reads the ops tag; dump shows every fn's ops by name and names a describe without them as missing; fixture.I2cTarget.FEATURE_STRETCH removed.
    • No resume (D1, core §6.2, §6.4, §9): end, lease expiry and force release everything the session created; only open takes the lock. Host.open(lease_ms, force=, owner=) always opens a new session under a new random id (the session= argument is gone) and returns Opened(lease_ms, boot_id) (resumed / swept and host.RESUMED gone). host.Expired and message.EXPIRED are gone: a request of an ended session raises NoSession, which leaves Host.session None and moves Host.epoch; end() does the same. A changed boot_id still drops the fn names; an open answering resumed 0 no longer exists. host.rejection(result) takes no lease.
    • oep.link (D3, oep-if-link): the link test and port_speed moved out of fn 0 into the optional interface oep.link (source 0x01: length(u32) -> len(u16) data; sink 0x02: count(u16) data -> empty; port_speed 0x03, offered when its ops set it). message.OP_LINK_SOURCE / OP_LINK_SINK removed; core.LINK_SOURCE / LINK_SINK / LINK_PORT_SPEED / link_fn / link_source_request / link_sink_request / link_source_data; core.link_speed, linktest and link.raise_speed find oep.link by name. link.raise_speed(hst, rates, record=, flows=, max_tries=) and SpeedReport (chosen, rate, trials with flows, skipped, step_downs, retried, capped) are unchanged; a probe without oep.link, or whose oep.link does not offer port_speed, reports supported False with the reason. link._speed_port returns (fn, port, why).
    • The fake: every profile's fn carries its ops (fake.FakeProbe fills in every op of the interface's table when a profile gives none, fill_ops=False leaves it out; fake.ops_of(name, *without); esp32-v003's riscv-dm has no step and its i2c-target no stretch, as before), and offers reads them; every profile has oep.link as its last fn (esp32-v003's with port_speed). End / lapse / force release the session's plan, its shares of streams (a stream it was the last user of closes with detail session_ended, before the connection) and connections, subscriptions and capture-group bind; console streams stay the probe's per place and mechanism (readable until the next open there, which returns the number with its position and marks); an attach on a live combination returns the slot's connection; the resend table answers a resent end, never looks up an open. An item or request TLV longer than its form is unsupported when critical, else ignored. fake_serve and its reboot line work as before.
    • Console writes (oep-spec 4621eab, console §2 / §3): the fake takes at most the mechanism's send slot (DMDATA 3 bytes, dmseq 2, SDI 0) and nothing while earlier bytes are still in it (Endpoint.console_slot, Stream.slot; the target takes them at the probe's next poll or at console_take(sid); console_accept removed). ConsoleIO.write goes on from each answer's accepted and raises TimeoutError after stall_s (default 2 s) with nothing accepted (an SDI console never accepts).
    • Spec references in code follow the split (core §3.x / §5.1 / §16 -> transports §x, core §3.5 -> oep-if-link §3). tests/test_vectors.py runs sessions.json step by step and every ops.json case on the fake in the state it names, and the client's own request and reading where it has the op; tests/test_no_resume.py; the hw session test checks no_session instead of expired / resumed.
  • (JA) 破壊的(wire と API): oep-spec b69ec26〜4621eab、2026-10-06 の単純化(docs/v1-simplification-proposal-2026-10-06.ja.md の全項目を適用。凍結までは日本語の文が作業の文)。README に実装する仕様の commit(4621eab。v0.x のタグはまだ無い、versioning §6)を書いた。registry とベクタを同期(sessions.json と ops.json が新しい)。
    • 符号(core §2.2、§2.3、§4.1): どの要求も 10 byte の見出し 1 つ role 0x01, corr(u16), fn(u16), op(u8), session_id(u32)、0 = セッションなし(role 0x81 は無い。message.Request.session は int で None は 0 とする。REQUEST_HEADER は 10、ROLE_SESSION / SESSION_BYTES は削除)。TLV は 1 つの形 tag(u8) len(u16) value(TLV_LEN_LONG、TLV_SHORT_MAX、BadTlv は削除)。並びは要素に長さを置かない count × 要素(list、connections、scan、marks、streams、segments、probe.config の state。Reader.element と message.element は削除)。固定の形は閉じる: probe.config の idle は 6 byte(drive_kind 2 = 既定の段、value 0。入力の idle は kind 2)、スロットは attach のすぐ後に boot_reset を持ち錠で終わる、slot_state は tid の前に reset_at_ns、bind のストリームは 1 つ 3 byte。fixture.Drive.default()。
    • ops(core §1.2、§7.4): どの fn の describe も ops tag(0x09、base + bitmap)を持ち、fn が持つ op の唯一の宣言になる。任意の op を宣言していた features のビット(riscv-dm の block / run / reset / step、i2c-target の stretch、logic / analog の query と force、capture-group の force)と fn 0 の describe 0x4E を置き換える。新しく: core.ops(hst, fn)、core.offers(hst, fn, op)、Interface.ops() / .offers(op)、core.require / core.not_offered(送らずに、probe が答えるのと同じ detail unknown_operation の Rejected を上げる。クライアント自身は今も送って probe に答えさせる)。catalog.pack_ops / ops_tlv / unpack_ops、Description.ops。RiscvDm.declared() は ops tag を読む。dump はどの fn の ops も名前で出し、ops の無い describe を欠けとして名指す。fixture.I2cTarget.FEATURE_STRETCH は削除。
    • 再開なし(D1、core §6.2、§6.4、§9): end、lease の期限切れ、force はセッションが作ったものをすべて解放し、ロックを取るのは open だけ。Host.open(lease_ms, force=, owner=) はいつも新しい乱数の id で新しいセッションを開き(session= の引数は無い)、Opened(lease_ms, boot_id) を返す(resumed / swept と host.RESUMED は無い)。host.Expired と message.EXPIRED は無い: 終わったセッションの要求は NoSession を上げ、Host.session を None にし Host.epoch を進める。end() も同じ。boot_id が変われば今どおり fn の名前を捨てる。resumed 0 の open はもう無い。host.rejection(result) は lease を取らない。
    • oep.link(D3、oep-if-link): 線の試験と port_speed は fn 0 から任意のインターフェース oep.link に移った(source 0x01: length(u32) → len(u16) data、sink 0x02: count(u16) data → 空、port_speed 0x03 は ops が立てるときだけ)。message.OP_LINK_SOURCE / OP_LINK_SINK は削除。core.LINK_SOURCE / LINK_SINK / LINK_PORT_SPEED / link_fn / link_source_request / link_sink_request / link_source_data。core.link_speed、linktest、link.raise_speed は oep.link を名前で探す。link.raise_speed(hst, rates, record=, flows=, max_tries=) と SpeedReport(chosen、rate、flows を持つ trials、skipped、step_downs、retried、capped)は変わらない。oep.link の無い probe、または port_speed を持たない oep.link は、理由を付けて supported False。link._speed_port は (fn, port, why) を返す。
    • 偽の probe: どの profile の fn も ops を持ち(profile が書かなければ fake.FakeProbe がそのインターフェースの表の op をすべて立てる。fill_ops=False で付けない。fake.ops_of(name, *without)。esp32-v003 の riscv-dm は今どおり step を、i2c-target は stretch を持たない)、offers はそれを読む。どの profile も最後の fn に oep.link を持つ(esp32-v003 のものは port_speed つき)。end / 期限切れ / force はセッションの plan、ストリームの持ち分(最後に使っていたストリームは connection より先に detail session_ended で閉じる)と connection の持ち分、購読、capture-group の bind を解放する。コンソールのストリームは場所と mechanism ごとの probe のもの(そこで次に open されるまで読め、その open は番号を位置とマークごと返す)。生きている組への attach はスロットの connection を返す。送り直した end には表から答え、open は表で引かない。形より長い項目と要求の TLV は、critical なら unsupported、そうでなければ無視。fake_serve とその reboot の行は今どおり動く。
    • コンソールの write(oep-spec 4621eab、console §2 / §3): 偽の probe は mechanism の送り枠(DMDATA 3 byte、dmseq 2、SDI 0)までしか受けず、前のバイトが枠に残る間は受けない(Endpoint.console_slot、Stream.slot。target は probe の次の poll か console_take(sid) でそれを取る。console_accept は削除)。ConsoleIO.write は応答の accepted の続きから送り、stall_s(既定 2 秒)何も受けなければ TimeoutError(SDI のコンソールは受けない)。
    • コードの中の仕様の参照は分割に合わせた(core §3.x / §5.1 / §16 → transports §x、core §3.5 → oep-if-link §3)。tests/test_vectors.py は sessions.json を 1 段ずつ、ops.json の全部の場合をそれぞれが書く状態の偽の probe で走らせ、op を持つものはクライアント自身の要求と読み方も確かめる。tests/test_no_resume.py。実機の session の試験は expired / resumed の代わりに no_session を確かめる。
  • (EN) Fix: a bare usb target looks at every device on the project's VID:PID, not the first one libusb lists. With several probes attached, a CDC-only one (an RP2350) listed first made it give up the vendor / HID path at that device without trying the others. Now link.usb_probes() lists each device once, whatever ways in it has (vendor bulk and HID from libusb / hidapi, CDC ports from pyserial, merged by USB serial or location), as link.UsbProbe (unit_id, ways, ports, location). Exactly one probe: it is opened (vendor, then HID, else its CDC port; a probe with several CDC ports asks for one). More than one: nothing is opened and link.SeveralProbes (a ValueError, .probes) lists each with its unit id and ways in and asks to name one (usb:<unit_id>, or its serial port); the oep command prints that list in its one line. None: the "no way in" error as before. usb:VID:PID[:SERIAL] and usb:<unit_id> are unchanged.
  • (JA) 修正: usb だけの target は、libusb が最初に返す機器だけでなく、プロジェクトの VID:PID のすべての機器を見る。複数のプローブがつながっていて CDC だけのもの(RP2350)が最初に返ると、その機器で vendor / HID の道をあきらめ、ほかを試さなかった。link.usb_probes() は口の種類にかかわらず機器ごとに 1 つ(vendor bulk と HID は libusb / hidapi、CDC のポートは pyserial から。USB の serial か location で 1 つにまとめる)を link.UsbProbe(unit_id、ways、ports、location)で返す。プローブがちょうど 1 つならそれを開く(vendor、次に HID、なければ CDC のポート。CDC のポートが複数あれば 1 つを指定させる)。2 つ以上なら何も開かず、link.SeveralProbes(ValueError、.probes)が各プローブの unit id と口を並べて、1 つを指定するよう求める(usb:<unit_id> かそのシリアルポート)。oep コマンドの 1 行にもその一覧が出る。0 なら今まで通り "no way in" のエラー。usb:VID:PID[:SERIAL] と usb:<unit_id> は変わらない。
  • (EN) fake_serve: a line reboot on stdin reboots the fake probe mid-session with a new random boot_id (the session table, the resend table, connections, streams, subscriptions, the plan and the unsaved settings go; the saved settings apply again; the pty / TCP connection stays open, a serial port's half-read frame and unsent answers are lost). stderr says fake_serve: rebooted, boot_id 0x........; any other line is ignored with a message on stderr. stdin is read without blocking the serving. --keep-on-eof: the end of stdin no longer ends the program (by default it still does). Endpoint.reboot now also forgets the revision in use, and keeps the simulation's own knobs (capture_slipped, uart_clock_hz) that it used to reset.
  • (JA) fake_serve: stdin に reboot の 1 行で、偽の probe がセッションの途中で新しい乱数の boot_id で再起動する(セッションの表、送り直しの表、接続、ストリーム、購読、plan、保存していない設定は消え、保存した設定はもう一度当たる。pty / TCP の接続は開いたまま、シリアルの口の読みかけのフレームと送っていない答えは失われる)。stderr に fake_serve: rebooted, boot_id 0x........ を出し、ほかの行は stderr に一言出して無視する。stdin は配信を止めずに読む。--keep-on-eof: stdin の終わりでプログラムを終えない(既定では今まで通り終える)。Endpoint.reboot は使っている revision も忘れ、これまで消していたシミュレーションの設定(capture_slipped、uart_clock_hz)は残す。
  • (EN) The oep command (dump, config, speed, linktest, pins): a probe that cannot be opened (no device on the project's VID:PID, no port, not an OEP probe, a value the host does not use) ends it with one line on stderr instead of a traceback; for a usb target the line adds a hint: install the usb / hid extras, or name the port. A bare usb whose CDC fallback finds several ports lists them (name one), and says so when it finds none.
  • (JA) oep コマンド(dump、config、speed、linktest、pins): 開けないプローブ(プロジェクトの VID:PID の機器がない、ポートがない、OEP のプローブでない、ホストが使わない値)では、トレースバックではなく stderr に 1 行を出して終わる。usb の指定では、その行にヒント(usb / hid の extras を入れるか、ポートを指定する)を添える。素の usb の CDC への切り替えでポートが複数あればそれらを挙げ(1 つを指定する)、1 つもなければそう言う。
  • (EN) What every probe must give (core §1.2, §7.1, §7.5; oep-spec docs/conformance.md section 1) as one list: dump.required_missing also names describe's discoverable of fn 0 (every probe sends it, 0 when it is not on the project's VID:PID) and a confirm transport index that names no transport of fn 0's describe (0xFF, a relaying broker, names none and passes); dump.required_of(call, confirm) runs it on a probe lock-free. tests/hw has a required test after identity that fails naming each item missing (a probe without confirm's transport TLV or discoverable passed tests/hw before). The fake sends discoverable 0 on a probe not on the project's VID:PID (esp32-v003), as §7.5 says, instead of leaving it out.
  • (JA) どのプローブも出すべきもの(core §1.2、§7.1、§7.5。oep-spec docs/conformance.md 1 節)を 1 つの一覧にした: dump.required_missing は fn 0 の describe の discoverable(どのプローブも出す。プロジェクトの VID:PID でなければ 0)と、fn 0 の describe のどの経路も指さない confirm の transport の index(0xFF は中継ブローカーで、どれも指さず通る)も挙げる。dump.required_of(call, confirm) がそれをロックなしでプローブに対して行う。tests/hw に identity の次の required テストを足し、欠けたものを名指しして失敗する(confirm の transport TLV や discoverable のないプローブが、これまで tests/hw を通っていた)。fake はプロジェクトの VID:PID でないプローブ(esp32-v003)でも、§7.5 のとおり discoverable 0 を出す(省かない)。
  • (EN) Breaking: clipped analog values (oep-spec ab2045c, capture §1.2 rule 6). A raw value of 0 or 2^b - 1 (the converter's minimum or maximum code) means the input was at or beyond that end of the frontend's range, so AnalogCapture.millivolts(k, value) now returns None for it instead of a voltage (its type is float | None; a caller that did arithmetic on it gets a TypeError there, not a wrong number). values() still returns the raw codes unchanged. New: AnalogCapture.clipped(k, value) (0, CLIP_LOW = -1 at or below the low end, CLIP_HIGH = +1 at or above the high end; with a negative scale_nv code 0 is the high end), ends_millivolts(k) ((low, high) in mV, what a clipped value is shown against), clip_mask(k, values) and clip_counts(k, values) ((low, high)). The hardware test's analog record gains clipped and its mean_mv is None when every value is clipped at one end. The fake still sends raw codes (its square wave and the sine's troughs are at the codes' ends).
  • (JA) 破壊的変更: アナログの振り切れた値(oep-spec ab2045c、capture §1.2 規則 6)。生の値 0 と 2^b - 1(変換器の最小と最大のコード)は入力が frontend の範囲のその端か外にあったことを表すので、AnalogCapture.millivolts(k, value) はこの値に電圧ではなく None を返す(型は float | None。値で計算していた呼び出し元はそこで TypeError になり、誤った数にはならない)。values() は今までどおり生のコードをそのまま返す。新しく AnalogCapture.clipped(k, value)(0、低い端以下は CLIP_LOW = -1、高い端以上は CLIP_HIGH = +1。scale_nv が負ならコード 0 が高い端)、ends_millivolts(k)(mV の (低い端, 高い端)。振り切れた値を示すときの基準)、clip_mask(k, values) と clip_counts(k, values)((低, 高))。実機テストのアナログの記録に clipped が加わり、すべての値が一方の端に振り切れたときは mean_mv が None。fake は今までどおり生のコードを送る(矩形波と正弦波の谷はコードの端にある)。
  • (EN) Breaking: the project's own USB VID:PID, 1209:4F45 (oep-spec d50a84e: registry usb.project_vid / project_pid, core §3.3, §7.5). The registry synced (USB["project_vid"], USB["project_pid"]). link.PROJECT_VID_PIDS is ((0x1209, 0x4F45),), taken from the registry, and link.is_project_device is true for it alone. link.USB_VID / USB_PID, what a bare usb target opens, are that VID:PID. A bare usb with no vendor bulk or HID way in on it opens the one CDC port carrying it (link.project_serial_ports(): pyserial's list filtered by the VID:PID, as (port, USB serial); several ports: name one) - a CDC-only probe (RP2040 / RP2350) is found so. The temporary clues are gone: link.temporary_clue and TEMPORARY_IPRODUCT_PREFIX / TEMPORARY_VENDOR_INTERFACE / TEMPORARY_HID_USAGE_PAGE are removed and nothing looks at iProduct. A probe on any other VID:PID is opened only by its unit id (usb:<unit id>), usb:VID:PID or its port. The fake's USB profiles (p4-x035, p4-bench, rp2350-pins) say discoverable = 1; esp32-v003 (a UART bridge alone) still sends none. New: udev/70-oep-probe.rules (the USB device and hidraw of 1209:4F45 for the logged-in user and plugdev; installed with administrator rights, README).
  • (JA) 破壊的変更: プロジェクトの USB の VID:PID 1209:4F45(oep-spec d50a84e: registry の usb.project_vid / project_pid、core §3.3、§7.5)。registry を写し直した(USB["project_vid"]、USB["project_pid"])。link.PROJECT_VID_PIDS は registry から作る ((0x1209, 0x4F45),) で、link.is_project_device はこれだけに真を返す。usb だけの target が開く link.USB_VID / USB_PID はこの VID:PID。この VID:PID に vendor bulk も HID も無ければ、usb はこの VID:PID を持つ CDC の口を 1 つ開く(link.project_serial_ports(): pyserial の一覧をこの VID:PID で絞り、(口, USB の serial) を返す。複数あれば口を名指す)。CDC だけの probe(RP2040 / RP2350)はこれで見つかる。暫定の手がかりは無くなった: link.temporary_clue と TEMPORARY_IPRODUCT_PREFIX / TEMPORARY_VENDOR_INTERFACE / TEMPORARY_HID_USAGE_PAGE を削除し、iProduct を見るところは無い。ほかの VID:PID の probe は unit id(usb:<unit id>)、usb:VID:PID か口の名指しでだけ開く。fake の USB の profile(p4-x035、p4-bench、rp2350-pins)は discoverable = 1 を出す。esp32-v003(UART bridge だけ)は今までどおり出さない。新しく udev/70-oep-probe.rules(1209:4F45 の USB device と hidraw を、ログインしている利用者と plugdev に開かせる。入れるには管理者の権限が要る。README)。
  • (EN) The rules since the 2026-10-02 changes (oep-spec 2e70f40, 975d88c, 598bb26, 8d91db0, 73a0c37, b4b08f1, 40291a4; docs/v1-rule-change-proposal-2026-10-06.md). Registry and test vectors synced (oep-spec fb7777e: LIMITS["max_op_ms_max"] 600000, mark_detail_reset 2 reserved, rvswd / swio scan_kind riscv-dm only, spi-target cs_setup_ns); tests/vectors/discovery.json is checked like the other vectors (list, describe of fn 0 and past the end, the order-1 refusals of the smallest probe: the fake's answers byte for byte, the host's reading). Fake: an op the interface does not define, or an optional op the probe does not declare (riscv-dm read_block / write_block / run / reset / step by features bits 0-3, capture query / force and the group's force, i2c-target stretch, probe.config save / erase without storage), is unknown_operation at core §4.3 order 1, before session_required (Endpoint.offers; it answered session_required, and served undeclared riscv-dm ops); plan_apply and probe.config set / unset check every malformed case, then the fns inside the payload (unsubscribe too), then unsupported for the whole request (C-21), and capture-group bind and a bound track's start check their form before the state; a plan item's unsupported names the item's tag; non-request roles and short requests are discarded (C-36); the clock is ns since boot at the resolution given (Endpoint(now_ns=...); fake_serve: time.monotonic_ns), never back, from 0 at reboot(), which now draws a boot_id when given none (C-31, C-19; fake_serve draws its own); confirm's bounds and max_op_ms 1..600000 are checked when an Endpoint is built (C-20, C-47); a UART bridge / TCP transport names interface 0xFF (C-41); every channel not reserved is parked at boot (△12); a riscv-dm op whose target does not answer fails with status line and rests the lines undriven until one succeeds (pin_state wire-free; debug §2); the attach reset TLV on an output-idle channel is unavailable cause 5 holder_kind 7; search_retries only when a bring-up ran; i2c-target refuses 0x00-0x07 / 0x78-0x7F (△5); a uart write without TX is unavailable cause 6 (△6); esp32-v003's spi-target declares cs_setup_ns 4000. Client: host.NotUsable for a confirm outside max_frame >= 64, window >= max_frame, max_inflight >= 1 (C-20) and a max_op_ms outside 1..600000 (C-47), nothing more sent; the transfer time counts min_max_frame until a confirm answer, then the latest one's max_frame, the link's own confirms included (N-1); an open with the id used last answered resumed = 0 drops the name -> fn cache and the describes (C-19); fn 0 heartbeats are read (Host.heartbeat_seen, uptime_ns) and their boot_id watched, as the comments had claimed; short answers / events / data are broken frames (C-36); a resend that gets no answer raises link.TransportFailed and the next request first recovers with a §5.1 confirm on every kind of frame, COBS included, its boot_id checked (recover_transport; C-38); RiscvDm.declared(); SpiTarget.cs_setup_ns (shown by oep dump); I2cTarget.configure refuses the reserved addresses before sending; ProbeConfig.state() documents the last page's storage (PC-9). The host and probe guide section numbers in the code follow oep-spec's renumbering (host §2, §4, §6, §9, §17, §18.1, §19; probe §6, §8); tests/hw/README points to release-testing.md.
  • (JA) 2026-10-02 の変更の後の規則(oep-spec 2e70f40、975d88c、598bb26、8d91db0、73a0c37、b4b08f1、40291a4。docs/v1-rule-change-proposal-2026-10-06.ja.md)。registry とテストベクタを同期(oep-spec fb7777e: LIMITS["max_op_ms_max"] 600000、mark_detail_reset の 2 は予約、rvswd / swio の scan_kind は riscv-dm だけ、spi-target の cs_setup_ns)。tests/vectors/discovery.json もほかのベクタと同じく調べる(いちばん小さい probe の list、fn 0 と終わりの先の describe、順序 1 の断り: fake の応答をバイト単位で、host の読み方も)。fake: インターフェースが定めない op と、probe が宣言しない任意の op(riscv-dm の read_block / write_block / run / reset / step は features の bit 0〜3、capture の query / force と group の force、i2c-target の stretch、保存の無い probe.config の save / erase)は core §4.3 の順序 1 で session_required より先に unknown_operation(Endpoint.offers。これまでは session_required を返し、宣言の無い riscv-dm の op も動いていた)。plan_apply と probe.config の set / unset は、要求全体で malformed をすべて調べ、次に payload の中の fn(unsubscribe も)、次に unsupported を調べる(C-21)。capture-group の bind と group に入った track の start は状態より先に形を調べる。plan の項目の unsupported は項目のタグを返す。要求でない role と短い要求は捨てる(C-36)。時計は起動からの ns で、与えた分解能(Endpoint(now_ns=...)。fake_serve は time.monotonic_ns)、戻らず、reboot() で 0 から。reboot() は boot_id を渡さなければ引く(C-31、C-19。fake_serve も自分で引く)。confirm の範囲と max_op_ms 1〜600000 は Endpoint を作るときに調べる(C-20、C-47)。UART bridge / TCP の経路は interface 0xFF(C-41)。reserved でない channel は起動で全部空きの状態に置く(△12)。target が答えない riscv-dm の op は status line で失敗し、成功するまで線を駆動しない(pin_state は wire-free。debug §2)。出力の idle の channel を名指す attach の reset TLV は unavailable cause 5 holder_kind 7。search_retries は立ち上げをしたときだけ。i2c-target は 0x00〜0x07 / 0x78〜0x7F を断る(△5)。TX の無い uart の write は unavailable cause 6(△6)。esp32-v003 の spi-target は cs_setup_ns 4000 を宣言する。client: confirm が max_frame >= 64、window >= max_frame、max_inflight >= 1 の外(C-20)と、max_op_ms が 1〜600000 の外(C-47)なら host.NotUsable で、それ以上送らない。転送時間は confirm の応答が来るまで min_max_frame、その後は最新の max_frame で数える(link 自身の confirm も。N-1)。前に使った id の open が resumed = 0 なら名前 → fn の cache と describe を捨てる(C-19)。fn 0 の heartbeat を読み(Host.heartbeat_seen、uptime_ns)、その boot_id を見る(コメントにはあったが、していなかった)。短い応答・event・data は壊れたフレーム(C-36)。送り直しにも応答が無ければ link.TransportFailed を上げ、次の要求の前にどのフレームの形でも(COBS も)§5.1 の confirm で立て直し、その boot_id を見る(recover_transport。C-38)。RiscvDm.declared()。SpiTarget.cs_setup_ns(oep dump が出す)。I2cTarget.configure は予約のアドレスを送る前に断る。ProbeConfig.state() は最後のページの保存の状態を取ると書いた(PC-9)。コードの中の host / probe 開発ガイドの節の番号を oep-spec の付け直しに合わせた(host §2、§4、§6、§9、§17、§18.1、§19。probe §6、§8)。tests/hw/README は release-testing.md を指す。
  • (EN) The 2026-10-02 rule changes (oep-spec 09622ef..e9cd891, docs/v1-rule-change-proposal-2026-10-02.md). Registry synced (role_assignment is the number 0x10 and core.plan_apply sends it critical; the target_id schemes in registry.COMMON; registry.REFERENCE["max_op_ms"]; the analog trigger enum without level / edge; new limits, timing values, TLVs and enums). Fake: a value a later revision may define is unsupported (list flags, attach method, reset mode, read from, gpio mode, i2c-target mode, uart format, port_speed step, probe.config idle mode / drive_kind / slot enums / bind stream kind / lock scheme; an undefined gpio drive kind ignored or unsupported when critical); a request's form, then its values, then the state (core §4.3), order-1 refusals before the resend table; a non-repeating request TLV twice or short is malformed, a longer one unsupported (critical) or ignored; ignored at most 16 entries with 0x00 as the 16th, room kept; confirm's transport TLV, the revision per transport, min > max malformed, the supported range in the refusal; rejected answers remembered and the lease restarted by them, lease 1000-60000; session_id 0, open as 0x81, booleans and text malformed; plan ops unknown_operation without plan roles; verify_ms 0 in a try malformed; count = 0 leaves out idle-item channels, a named output idle is unavailable cause 5 holder_kind 7; undeclared scan combinations unsupported with their index, attach's pins tag as received, a swio swclk other than 0xFFFF unsupported; found = version >= 2 and != 15; halt / step failures (step_left); search_retries; pin_state (MISO only while CS is active, i2c-target open-drain and with_i2c_pullups, plans change no pin, an analog plan on an output idle refused); capture samples rounded down, mode / rate / trigger refused as received, capture-group state and start checks, a read past the write position; probe.config's line search with the firmware labels as step (c), no_pull, an item's channel, label text, a saved bind on a non-serial port (reason 2), an item's unsupported naming its tag; the profiles reserve only the probe's own channels; fake_serve's TCP listener is a TCP transport, an over-long length closes it. Client: every wait at least core §4.4's floor (argument time incl. the attach / scan budgets + 1000 ms + a serial port's transfer time); serial ports 8N1, DTR / RTS asserted; later confirms (the link's own, dump's) ask for the revision in use; limits["transport"] and port_speed on the transport this host came on; an x- unit_id keys no speed record and names no USB device; no stall resync on TCP, 250 ms after the last write before a resync's confirm; find_line with firmware labels; answer text shown without control characters, the owner cut on a character; Tail.more_ignored; dump names what core §1.2 requires and is missing; capture's critical TLVs and blocking_ms; Wire.search_retries, riscv.StepError, I2cTarget.pullup_ohms, describe's capture mode with background. tests/vectors (the spec's vectors, synced by tools/sync_registry.sh) checked by tests/test_vectors.py.
  • (JA) 2026-10-02 の規則の変更(oep-spec 09622ef..e9cd891、docs/v1-rule-change-proposal-2026-10-02.ja.md)。registry を同期(role_assignment は番号 0x10 で core.plan_apply が critical で送る。target_id の scheme は registry.COMMON。registry.REFERENCE["max_op_ms"]。analog の trigger の enum から level / edge が消えた。limits・timing・TLV・enum の追加)。fake: 後の revision が定めうる値は unsupported(list の flags、attach の method、reset の mode、read の from、gpio の mode、i2c-target の mode、uart の format、port_speed の step、probe.config の idle の mode / drive_kind / slot の enum / bind の stream の kind / lock の scheme。gpio の drive の未定義の kind は無視、critical なら unsupported)。要求は形、値、状態の順(core §4.3)、order 1 の拒否は送り直しの表より前。繰り返さない要求の TLV が 2 つ・短い値は malformed、長い値は unsupported(critical)か無視。ignored は 16 項目までで 16 番目に 0x00、その場所を空けておく。confirm の transport TLV、経路ごとの revision、min > max は malformed、拒否に扱える範囲。断った応答も覚え、lease もそれで延び、lease は 1000〜60000。session_id 0、0x81 の open、真偽値と文字列は malformed。plan の役割が無ければ plan の op は unknown_operation。try の verify_ms 0 は malformed。count = 0 は idle の項目がある channel を外し、名指しされた出力の idle は unavailable cause 5 holder_kind 7。宣言に無い scan の組は位置つきで unsupported、attach の pins は受けた tag、swio の swclk が 0xFFFF でなければ unsupported。見つかった = version が 2 以上で 15 でない。halt / step の失敗(step_left)。search_retries。pin_state(MISO は CS が有効な間だけ、i2c-target はオープンドレインで with_i2c_pullups、plan はピンを変えない、出力の idle への analog の plan は断る)。capture の samples は切り下げ、mode / rate / trigger は受けた tag で断る、capture-group の状態と start の前の確かめ、書き込み位置より先の read。probe.config の線の探し方の手順 (c) に firmware のラベル、no_pull、項目の channel、label の文字列、serial port でない口への保存済みの bind(reason 2)、項目の unsupported はその tag を返す。profile の reserved は probe 自身の channel だけ。fake_serve の TCP の待ち受けは TCP の経路で、長すぎる長さは接続を閉じる。client: 応答はどれも core §4.4 の下限(attach / scan の予算を含む引数の時間 + 1000 ms + シリアルの口の転送時間)以上待つ。シリアルの口は 8N1 で DTR / RTS を立てる。2 回目からの confirm(link 自身と dump のも)は使っている revision を求める。limits["transport"] と、この host が来た経路での port_speed。x- の unit_id は速さの記録の鍵にも USB の機器名にもしない。TCP では止まったフレームの立て直しをしない、立て直しの confirm の前に最後の書き込みから 250 ms 待つ。find_line は firmware のラベルも探す。応答の文字列は制御文字を除いて見せ、owner は文字の境で切る。Tail.more_ignored。dump は core §1.2 が求めるもので無いものを示す。capture の critical な TLV と blocking_ms。Wire.search_retries、riscv.StepError、I2cTarget.pullup_ohms、describe の capture の mode と background。tests/vectors(spec のベクタ、tools/sync_registry.sh で同期)を tests/test_vectors.py で確かめる。
  • (EN) Fake: the transport list of fn 0's describe follows each TLV's index byte (core §7.5: index is the number designating the transport), not the TLV order; port_speed's port and a bind's port (probe.config §1.2) are checked against that index, so a probe declaring its transports out of order or with gaps is handled. Link: the link's own confirms (the §5.1 resync on length-prefixed ports, confirm_raw) ask for the revisions this client handles (1..1, core §7.1: the host sends the range it can handle), not 0..0xFF. Tests: transports out of TLV order and with gaps (port_speed accepted on the UART bridge's index, refused on the vendor bulk; a bind on the serial port's index, unsupported on the other), the range of the resync's and confirm_raw's confirms.
  • (JA) fake: fn 0 の describe の経路の並びは TLV の順ではなく各 TLV の index バイトに従う(core §7.5: index は経路を指す番号)。port_speed の port と bind の port(probe.config §1.2)はその index で調べるので、経路を順不同や飛び番で宣言する probe も扱える。link: link 自身の confirm(長さ前置きの経路での §5.1 の再同期、confirm_raw)は 0..0xFF ではなくこのクライアントが扱える revision(1..1、core §7.1: host は扱える範囲を送る)を求める。試験: TLV の順と違う経路と飛び番の経路(UART bridge の index での port_speed は受け付け、vendor bulk では拒否。serial port の index での bind は受け付け、もう一方は unsupported)、再同期と confirm_raw の confirm の範囲。
  • (EN) Fake, as oep-spec 9ed53e7 / 3cc50c8: describe of an fn the probe does not offer is rejected unknown_function (core §4.3), not malformed; the retry with reset (probe.config §3.1) counts in slot_state's last_try_at_ns (the attempt after the hold, so the next plain retry is retry_ms after it); a slot's pins without a connection are free for the idle state (probe.config §1: the state of a pin neither a plan nor a connection uses), so an idle change there applies at once (it used to wait for a release that, with the slot gone, never came). Tests: unset of an undeclared tag is unsupported with the tag as received; an idle set / unset applies at once on a free channel and at the next release on one a plan or a connection holds (mode and drive).
  • (JA) fake を oep-spec 9ed53e7 / 3cc50c8 に合わせた: 提供していない fn の describe は rejected unknown_function(core §4.3)で、malformed ではない。リセットでのやり直し(probe.config §3.1)は slot_state の last_try_at_ns に入る(保持の後の試み。次の普通のやり直しはその retry_ms 後)。接続の無いスロットのピンは idle の上では空き(probe.config §1: plan にも接続にも使われていないピンの状態)なので、そこへの idle の変更はすぐ効く(スロットが消えると来ない解放を待っていた)。試験: 宣言していない tag の unset は受け取ったままの tag で unsupported。idle の set / unset は、空いている channel にはすぐ、plan や接続が持つ channel には次の解放で効く(mode と強さ)。
  • (EN) fake_serve options for what other hosts' tests could not reach over TCP / a pty: --no-drive-levels (the profile's oep.fixture.gpio without drive_levels, fake.without_drive_levels), --silent-until-reset N (the N-th pin pair of the first wire has a target that answers nothing until a reset through its line, FakeTarget.silent_until_reset), --boot-reset (every --slot asks for the at-boot retry with reset, boot_reset 1) and --label CH=TEXT (a label item at boot, as if saved, e.g. 23=v003.nrst): the saved items go in at once, so the retry with reset happens at start. fake_serve.parse(argv) gives the options build takes. Tests: tests/test_drive_and_boot_reset.py. A new test checks that the fake lists ignored request TLVs on failed and partial answers too (core §2.3, oep-spec 9ed53e7; it already did): wire attach, riscv-dm step / read_block / write_block / run / resume / dmi, console write.
  • (JA) 外の host の試験が TCP / pty から届かなかったところのための fake_serve のオプション: --no-drive-levels(profile の oep.fixture.gpio から drive_levels を外す。fake.without_drive_levels)、--silent-until-reset N(最初の wire の N 番目のピンの組の target が、その線でリセットされるまで何も答えない。FakeTarget.silent_until_reset)、--boot-reset(どの --slot も起動直後のリセットでのやり直しを求める。boot_reset 1)、--label CH=TEXT(起動時の label の項目、保存したものとして。例 23=v003.nrst)。保存した項目はまとめて入るので、リセットでのやり直しは起動時に起きる。fake_serve.parse(argv) は build の受けるオプションを返す。試験: tests/test_drive_and_boot_reset.py。fake が failed と partial の応答にも ignored を載せること(core §2.3、oep-spec 9ed53e7。もとから載せていた)を確かめる試験を足した: wire の attach、riscv-dm の step / read_block / write_block / run / resume / dmi、console の write。
  • (EN) oep pins: the dead --reset-channel / reset_channel slot-field path is gone (a slot has no reset field; the reset line is the label <slot>.nrst, probe.config §1.3, oep-spec 6489c8e). The suggestion prints the slot line and the <slot>.nrst / <slot>.power_hi labels, and --save writes the slot and those labels; Report.reset_channel stays as the channel found. The README example shows the label lines instead of the old # reset_channel comment.
  • (JA) oep pins: 使われない --reset-channel / reset_channel のスロットのフィールドの道を消した(スロットに reset のフィールドは無く、リセットの線は label <スロット>.nrst。probe.config §1.3、oep-spec 6489c8e)。案内はスロットの行と label <スロット>.nrst / <スロット>.power_hi を出し、--save はスロットとその label を書く。Report.reset_channel は見つけた channel として残す。README の例は古い # reset_channel のコメントの代わりに label の行を示す。
  • (EN) The fake listed unknown non-critical request TLVs in the answer's ignored (core §2.3) only at some ops; about 50 ops (end, list, plan_apply, plan_release, subscribe / unsubscribe, the wires' scan / detach / connections, riscv-dm's dmi / halt / resume / read_block / write_block / run / step, console and uart read / mark / clear and more, logic / analog / capture-group start / stop / force / status / read / segments / release, probe.config save / erase / unset / state) dropped them. endpoint.Take now keeps the ignored tags and Endpoint._dispatch appends them to every completed answer in one place (no handler attaches them). A TLV in a describe or probe.config get request is now rejected malformed (core §7.3; get used to list it as ignored), plan_apply refuses tags 0x00 / 0x7F / 0xFF as malformed, and probe.config set's unsupported names an unknown item's tag as received (with its critical bit). tests/test_request_tails.py sends an unknown TLV, non-critical and critical, with every op of every interface the fake serves.
  • (JA) fake は、要求の未知の non-critical の TLV を応答の ignored に載せる(core §2.3)のを一部の op でしかしておらず、約 50 の op(end、list、plan_apply、plan_release、subscribe / unsubscribe、wire の scan / detach / connections、riscv-dm の dmi / halt / resume / read_block / write_block / run / step、console と uart の read / mark / clear ほか、logic / analog / capture-group の start / stop / force / status / read / segments / release、probe.config の save / erase / unset / state)で落としていた。endpoint.Take が ignored の tag を持ち、Endpoint._dispatch が完了したすべての応答に 1 か所で付ける(handler は付けない)。describe と probe.config の get の要求に TLV があれば rejected malformed にした(core §7.3。get は ignored に載せていた)。plan_apply は tag 0x00 / 0x7F / 0xFF を malformed で断り、probe.config の set は未知の項目の unsupported に受け取ったままの tag(critical の bit 付き)を載せる。tests/test_request_tails.py は fake が持つすべての interface のすべての op に未知の TLV を non-critical と critical で付けて送る。
  • (EN) oep-spec ee562c3: gpio output strength and the at-boot retry with reset. Registry synced (hash 29f3ea526760a418). Client: fixture.Drive (Drive.level(n) / `Drive.max_ma(m