feat(napi): generate CommandCore mutable handle class via fluessig#318
Conversation
5651c02 to
86154cd
Compare
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: 73/180 (40.6%) · ai: 81/1296 (6.3%) · 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 |
396d767 to
318fdb9
Compare
Swap the hand-written `#[napi] CommandCore` package-manager command-flow handle to the fluessig-generated surface. This is the first mutable-state handle class to migrate: the boxed `CommandFlowMachine` carries cross-step state (`start`/`advance` take `&mut self`), while fluessig's generated handle methods take `&self`. The bridge is interior mutability in the core impl: `CommandCoreImpl` holds the machine behind a `std::sync::Mutex`, and its `&self` trait methods lock and drive the `&mut self` machine. - Author the `CommandCore` ctor interface + `start`/`advance` ops in `schema/api.json` (catalog.json needs no change — no new DTO/scalar). - Move the command-flow driver logic (params parsing, `build_machine`, `step_to_json`) into `core_impl.rs` as `CommandCoreImpl`, reproducing the pre-swap error messages verbatim through `anyhow` so the thrown JS reasons are byte-identical. - Regenerate `src/generated.rs`; delete the hand-written `src/command_core.rs` and its `mod` in `lib.rs`. The generated `.d.ts` block, `.js` export, and nativeBinding entry for `CommandCore` are byte-identical to the pre-swap build (only their file position relocates into the generated-module region, as with the prior KeybindingsManagerCore swap). Oracle `test/package-manager.test.ts` passes 116/116 against the locally built addon.
86154cd to
0e44260
Compare
What
First mutable-state opaque-handle class swap in the pidgin-napi → fluessig campaign: moves the hand-written
#[napi] pub struct CommandCore(backingpackages/coding-agent/src/core/package-manager.ts) onto the fluessig-generated napi surface. Module 6 proved an immutable handle (KeybindingsManagerCore); this is the strategic prove that the pattern extends to a handle carrying cross-call mutable state — the shape shared by 8 of the 11 Bucket-D classes (~79 of the remaining exports).Stacked on
napi-fluessig/keybindings-manager(the current stack tip).The mutable-state bridge
The boxed
CommandFlowMachinecarries step state across calls — itsstart/advancetake&mut self. But fluessig's generated handle methods always take&self(the trait bound isSized + Send + Sync + 'static, and the class holdsArc<…Impl>). The bridge is interior mutability in the core impl:CommandCoreImplholds the machine behind astd::sync::Mutex<Box<dyn CommandFlowMachine + Send>>, and its&selftrait methods lock and drive the&mut selfmachine. A single JS caller never contends, so the lock is uncontended in practice.RefCellis not an option (the trait requiresSync);Mutexis the shim.The generated
.d.ts/.jsare unaffected by this — self-mutability isn't in the TS type — so byte-parity holds.Before / After
Before — hand-written in
src/command_core.rs:After — authored as a
ctorinterface inschema/api.json(ctor op +start/advanceunary ops), regenerated byregen.shintosrc/generated.rsas#[napi] pub struct CommandCore { core: Arc<CommandCoreImpl> }with the ctor building the core via the generatedCommandCoreCoretrait and each&selfmethod delegating throughself.core. The engine seam (CommandCoreImpl) lives insrc/core_impl.rs, holding the machine behind aMutexand routing to the same command-flow planning logic — reproducing the hand-written error messages verbatim throughanyhow.src/command_core.rsand itsmodinlib.rsare deleted.catalog.jsonneeds no change (no new DTO/scalar — both carriers arestring).How
CommandCore— the simplest mutable Bucket-D handle: ctor + only 2 methods, all-stringcarriers in both directions (no float / bytes / DTO to confound the mutable-state variable), fallible exactly like module 6, dedicatedstatus=nativeoracle.StdinBufferCorewas the alternative but adds a nullable-int ctor param + a DTO-array return;CommandCoreisolates the one new thing being proved.1fed77a) lowered the ctor interface to theArc<…Impl>handle class +…Coretrait with&selfmethods exactly as for the immutable case; the interior-mutabilityMutexincore_implcarries the&mut selfmachine, and theregen.shoutput was purely additive (44 insertions, 0 deletions).Gates
.d.ts: CommandCore class block byte-identical to the pre-swap baseline (JSDoc + all 3 members). Whole-file diff is a pure relocation of the block into the generated-module region..js: class entries byte-identical.module.exports.CommandCore = CommandCoreis byte-identical text; theCommandCorenativeBinding token is unchanged — the only delta is the symbol relocating from thelib.rsregion into the generated export block (every prior swap did the same; napi ordersindex.jsby crate registration order).cargo build -p pidgin-napi,cargo fmt --all --check,cargo clippy -p pidgin-napi --all-targets -- -D warnings— clean. codespell 0.git archive HEAD: 876 files, 0 errors, 1conformance/STEWARD.mdslop-prose WARN (pre-existing, allowed).packages/coding-agent/test/package-manager.test.tsagainst the locally built addon: 116/116 pass — critically, this exercises the ctor +start/advancestate transitions, validating the interior-mutability shim behaves identically.🤖 Generated with Claude Code
https://claude.ai/code/session_017xL2W7dkhPsTSRuXQNCDeY
Generated by Claude Code