feat(tui): generate InputCore via fluessig single_threaded handle (#80)#341
feat(tui): generate InputCore via fluessig single_threaded handle (#80)#341zmaril wants to merge 1 commit into
Conversation
Swap the hand-written `InputCore` napi class (packages/tui/src/components/ input.ts) for a fluessig-generated one, proving the #80 single_threaded (thread-confined `!Send`) handle path at the bumped pin (e056bb9): - Author the ctor + five sync ops + the `InputEvent` model in schema/ api.json, marking the interface `single_threaded: true`. fluessig lowers it to a THREAD-CONFINED handle: `pub struct InputCore { core: RefCell<InputCoreImpl> }` — no `Arc`, no `Send`/`Sync` — and the `InputCoreCore` trait sheds its `Send + Sync` supertraits (`Sized + 'static`) with `&mut self` ops reached through `borrow_mut()`. - This is exactly what lets a `!Send` core compile: `InputCoreImpl` (core_impl.rs) owns pi's `Input` (whose `on_submit`/`on_escape` are non-`Send` boxed closures) plus an `Rc<RefCell<InputEventState>>` event cell captured by those closures — the same wiring the deleted hand-written class used. - regen src/generated.rs (fluessig e056bb9); delete the hand-written `#[napi]` struct/impl + `InputEvent`/`InputEventState` from lib.rs; keep `pub mod generated;`. Self-mutability is invisible to the TS type, so the RefCell-vs-`&mut self` handle is byte-transparent: the generated `InputCore` class `.d.ts` block, the `InputEvent` interface, the `.js` export, and the nativeBinding entry are all byte-identical to the pre-swap build. pi's own test/input.test.ts oracle passes 35/35 against the locally-built addon. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xL2W7dkhPsTSRuXQNCDeY
Conformance smoke: agent + ai + tui
Headline is rust-backed: passing cases in files whose module-under-test is a native (Rust addon) module. Raw all-pass is shown secondary — it is inflated by unflipped TypeScript that passes without touching any Rust.
agent: 55/180 (30.6%) · ai: 74/1295 (5.7%) · tui: 375/678 (55.3%) rust-backed = passing / total tests run, per the manifest's per-native-row AttributionA pi test file is rust-backed iff the module it primarily exercises — its module-under-test — is Per-file decisions (from each native manifest row's
CLI conformance (black-box, against the pidgin binary)
The four repointed coding-agent CLI test files spawn the compiled pidgin binary via |
What
Swaps the hand-written
InputCorenapi class (packages/tui/src/components/input.ts) for a fluessig-generated one, proving the last unproven fluessig path in the pidgin campaign: #80single_threaded— a thread-confined,!Sendhandle that holds its core inRefCell<Impl>withoutArc/Send/Sync.Stacked on
napi-fluessig/bytes-image-mime(which stacks on the pin-walk to fluessige056bb9).Why InputCore
Of the three
!SendUI-state cores (TuiCore,InputCore,SelectListCore),InputCoreis the simplest: a zero-arg ctor, five all-sync / all-infallible ops, one small DTO (InputEvent), and a dedicatedstatus=nativeoracle (test/input.test.ts). It is genuinely!Send: it owns pi'sInput(whoseon_submit/on_escapeare non-SendBox<dyn FnMut>closures) plus anRc<RefCell<InputEventState>>event cell captured by those closures.The #80 path
Authoring the interface
single_threaded: truemakes fluessig lower it to:pub struct InputCore { core: RefCell<InputCoreImpl> }— noArc, noSend/Sync.pub trait InputCoreCore: Sized + 'static— sheds theSend + Syncsupertraits, so a!Sendcore implements it.&mut selfcore ops, reached from the handle's&selfmethods throughRefCell::borrow_mut().InputCoreImpl(incore_impl.rs) does the sameonSubmit/onEscapewiring the deleted hand-written class did — now legal to holdRc/RefCelldirectly since the trait carries noSendbound.Byte-transparency
Self-mutability isn't in the TS type, so
RefCell-vs-&mut selfis invisible at the boundary. The generatedInputCoreclass.d.tsblock, theInputEventinterface, the.jsexport, and the nativeBinding entry are byte-identical to the pre-swap build — the only file movement isInputEventand theInputCoreexport relocating from the hand-written region into the generated cluster (identical tokens).Gates
cargo build -p pidgin-napi,cargo fmt --all --check,cargo clippy -p pidgin-napi --all-targets -- -D warnings— clean (no RefCell-layer complaints).git archive HEAD— 0 errors.test/input.test.tsoracle against the locally-built addon — 35/35 pass.Generated files (
src/generated.rs) are regenerated viaregen.shagainst fluessige056bb9; not hand-edited.🤖 Generated with Claude Code
https://claude.ai/code/session_017xL2W7dkhPsTSRuXQNCDeY
Generated by Claude Code