• src/syncterm/WrenTODO.mdsrc/syncterm/Wren.adoc src/syncterm/scripts/au

    From Deucе@VERT to Git commit to main/sbbs/master on Sun May 3 00:25:42 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/bdd490904e413f949158803e
    Added Files:
    src/syncterm/WrenTODO.md
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/auto/connected/sftp_pubkey.wren src/syncterm/scripts/sftp_app.wren sftp_queue.wren syncterm.wren ui_app.wren ui_button_test.wren ui_checkbox_test.wren ui_demo.wren ui_draw_test.wren ui_form_test.wren ui_help_test.wren ui_input_test.wren ui_list.wren ui_list_test.wren ui_menubar.wren ui_menubar_test.wren ui_popup_test.wren ui_radio.wren ui_radio_test.wren ui_spinbox_test.wren ui_statusbar_test.wren ui_style_test.wren ui_widget.wren ui_widget_test.wren wrentest.wren src/syncterm/wren_bind.c wren_bind_conn.c wren_bind_conn.h wren_bind_fs.c wren_bind_fs.h wren_bind_internal.h wren_bind_screen.c wren_bind_screen.h wren_bind_sftp.c wren_bind_won.c wren_bind_won.h wren_host.c wren_host_internal.h
    Log Message:
    SyncTERM: Wren API alpha-polish pass

    Working from the WrenTODO.md audit, applied the resolutions across
    sections A-E that landed as code or documentation changes. Every
    item is recorded in WrenTODO.md with its rationale + the
    alternatives considered and rejected.

    Highlights:

    Errors
    - Polymorphic `Error` / `ScriptError` base; `FileError`, `WONError`,
    `ConnError` mirror `SFTPError`'s shape. Bucket B/C abort sites
    converted to typed errors; OOM stays as abort.

    API consistency
    - `SFTP.read(fiber, handle, count, offset)` swapped to match
    `File.readBytes(count, offset)`. `Conn.recv`/`peek` parameter
    renamed to `count`.
    - `Host.uploadDir` removed; replaced with `Host.uploadPath` (String).
    All uploads now go through `Host.pickFile` / `Host.pickFiles`
    (consent-token-backed); `sftp_queue` requires a token.
    - `Container.focusedIndex` / `ListView.selected` /
    `MenuBar.focusedItem` / `RadioGroup.selected`+`cursor` use `null`
    for "nothing selected" instead of `-1` at the API boundary.

    App + screen lifecycle
    - `Screen.modalRun(fn)` — atomic Screen.save + CTerm.suspended +
    restore wrapper; `sftp_app.run` and 10 ui_demo methods converted.
    - `App.releaseFocus` / `App.restoreFocus` — defocus the App's
    foreground tree around blocking host UIs (filepicker) so the
    underlying widgets aren't drawn focused while another widget owns
    the screen.
    - `CustomCursor.preserve(fn)` / `VideoFlags.preserve(fn)` snapshot
    helpers; both classes' static-vs-instance distinction documented
    with the chained-static-writes non-atomicity caveat.

    Sequence / data shape
    - `Console.entries` Sequence view (Console itself stays static;
    Sequence helpers need an instance).
    - `Surface.rows` / `Surface.cols` Sequence-of-Sequence views; row-
    major linear iteration order pinned in docs.
    - `Cell.eqContent(other)` named structural-equality method (NOT a
    `==` override — Cell stays foreign-identity-equal); ignores the
    BG dirty bit, false if either cell flies pixel-graphics.

    Doc reframes
    - Async pattern (`||`-yield) presented as the canonical idiom with
    the three single-source-fiber providers (hook handler / SFTP queue
    worker / `App.runChild` child) listed up-front.
    - `Hyperlinks` documented as a typed-ID lookup, NOT a Map (foreigns
    cannot be Map keys per `wren_value.h:880-888`).
    - `Cell` / `CTerm` accessor relationship clarified (different
    scopes, not duplicate views).
    - `toString` on debug-decorated classes documented as not-part-of-
    the-contract once, up front under `== Object Model`.

    Wrentest gained ~18 cases covering the new APIs and the null-
    boundary changes.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    ---
    ■ Synchronet ■ Vertrauen ■ Home of Synchronet ■ [vert/cvs/bbs].synchro.net