Skip to content

feat(tui): generate word-navigation napi bindings via fluessig#308

Merged
zmaril merged 1 commit into
mainfrom
napi-fluessig/module5
Jul 21, 2026
Merged

feat(tui): generate word-navigation napi bindings via fluessig#308
zmaril merged 1 commit into
mainfrom
napi-fluessig/module5

Conversation

@zmaril

@zmaril zmaril commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Stacked on #307 (module 4, tui utils/width). Base branch is napi-fluessig/tui-utils, so this diff is just the two word-navigation.ts exports.

Before / After

The packages/tui/src/word-navigation.ts layer — findWordBackward and findWordForward — used to be hand-written #[napi] free functions in lib.rs. After this change they are generated from the fluessig api schema through crate::generated + the core_impl seam, routing into pidgin_tui::word_navigation's default-segmenter path.

The JS surface is unchanged: every symbol's .d.ts block (JSDoc included) and .js glue is byte-identical to the pre-swap origin/main baseline, and the dedicated conformance oracle (packages/tui/test/word-navigation.test.ts) passes 19/19.

Both ops are (text: string, cursor: int32) -> int32. Cursors are UTF-16 string indices, authored in the schema as int32 (fluessig → Rust i32 → JS number) and widened to the engine's usize at the seam. The pre-swap exports used u32 (also number in .d.ts), so int32 reproduces the JS-visible surface byte-for-byte. The three shim-delegated "atomic segments" tests (which fall back to pi's original when a JS options.segment/options.isAtomicSegment callback is supplied) continue to pass unchanged.

How

  • Schema: appended the 2 ops to crates/pidgin-napi/schema/api.json (shape: unary, infallible: true, readonly: true, bindings.node.name = exact JS export name). Params text: string, cursor: int32; return int32. catalog.json unchanged (no new DTOs).
  • Seam: added 2 delegations to impl PidginCore for PidginImpl in src/core_impl.rs, each routing to the same pidgin_tui::word_navigation fn the hand-written export called. The i32 cursor param/return is widened to the engine's usize, matching the pre-swap as usize/as u32 casts.
  • Regen: reran crates/pidgin-napi/regen.sh (fluessig pinned at 1fed77a) to regenerate src/generated.rs. Deleted the 2 hand-written exports from src/lib.rs.

Verification

  • Per-symbol .d.ts + .js byte-diff vs origin/main baseline: both symbols IDENTICAL (JSDoc blocks and module.exports glue); the two symbols moved into the generated block right after extractSegments, the same mechanical reorder as modules 2-4.
  • cargo build -p pidgin-napi, cargo fmt --all --check, cargo clippy -p pidgin-napi --all-targets -- -D warnings: clean.
  • codespell on touched files: 0 findings.
  • straitjacket v0.2.3 whole-tree scan of git archive HEAD: 877 files, 0 errors (1 pre-existing conformance/STEWARD.md slop-prose warning).
  • packages/tui/test/word-navigation.test.ts (19/19) against the locally-built addon: all pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_017xL2W7dkhPsTSRuXQNCDeY


Generated by Claude Code

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown

Conformance smoke: agent + ai + tui

  • pi_sha: 3da591ab74ab9ab407e72ed882600b2c851fae21
  • manifest_native_modules: 54

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.

Package rust-backed Native modules raw pass (secondary) Failing Skipped
agent 73/180 (40.6%) 7 180/180 0 0
ai 81/1296 (6.3%) 11 553/1296 3 740
tui 375/678 (55.3%) 14 678/678 0 0
smoke total 529/2154 (24.6%) 32 1411/2154 3 740

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 tests lists. The Native modules column counts modules served by the Rust addon. This is the agent+ai+tui smoke subset; the full baseline lives in committed conformance.json.

Attribution

A pi test file is rust-backed iff the module it primarily exercises — its module-under-test — is status=native in conformance/manifest.json. A package's rust-backed count is the passing cases in its rust-backed files. Transitive or infrastructure use does not count (e.g. ~30 ai files construct the native faux provider but test other things — only faux-provider.test.ts, whose subject is faux, counts). A file that substantially tests both a native and an original module is excluded rather than counted, so the number under-reports rather than over-claims.

Per-file decisions (from each native manifest row's tests list):

Native module Test file Decision
ai/api/anthropic-messages.ts test/anthropic-sse-parsing.test.ts counted
ai/providers/faux.ts test/faux-provider.test.ts counted
coding-agent/utils/ansi.ts test/ansi-utils.test.ts counted
coding-agent/utils/changelog.ts test/changelog.test.ts counted
coding-agent/utils/git.ts test/git-ssh-url.test.ts counted
coding-agent/core/tools/path-utils.ts test/path-utils.test.ts counted
tui/keys.ts test/keys.test.ts counted
tui/utils.ts test/truncate-to-width.test.ts, test/regression-regional-indicator-width.test.ts counted
coding-agent/utils/mime.ts test/image-process.test.ts mixed — excluded (2 of 3 cases test processImage/original)
coding-agent/utils/version-check.ts test/version-check.test.ts mixed — excluded (fetch cases mock the original; not separable per-file)
coding-agent/core/export-html/ansi-to-html.ts test/export-html-whitespace.test.ts mixed — excluded (asset-grep + tool-renderer/original dominate)
coding-agent/core/tools/truncate.ts no dedicated test (exercised via tools.test.ts, subject is the read/edit factory/original)
coding-agent/core/tools/edit-diff.ts no dedicated test (edit-tool tests' subject is the edit tool/original)

CLI conformance (black-box, against the pidgin binary)

  • CLI conformance: 15/15 pass against target/release/pidgin (pass delta ±0, fail delta ±0).
File Passing Failing Skipped
packages/coding-agent/test/session-file-invalid.test.ts 1 0 0
packages/coding-agent/test/session-id-readonly.test.ts 7 0 0
packages/coding-agent/test/startup-session-name.test.ts 2 0 0
packages/coding-agent/test/stdout-cleanliness.test.ts 5 0 0

The four repointed coding-agent CLI test files spawn the compiled pidgin binary via $PIDGIN_BIN instead of pi's own cli.ts. This is a separate signal from the module smoke table and is never folded into the per-package Native count.

@zmaril
zmaril marked this pull request as ready for review July 21, 2026 09:06
@zmaril
zmaril force-pushed the napi-fluessig/tui-utils branch from 24d23e8 to 825cd50 Compare July 21, 2026 21:33
Base automatically changed from napi-fluessig/tui-utils to main July 21, 2026 22:16
Swap the tui word-navigation exports (`findWordBackward`, `findWordForward`)
from hand-written `#[napi]` free functions to fluessig-generated bindings,
routing through the `PidginCore` trait + `core_impl` seam like modules 1-4.

Both ops are `(text: string, cursor: int32) -> int32`; cursors are UTF-16
string indices authored as `int32` (JS `number`) and widened to the engine's
`usize` at the seam, delegating into `pidgin_tui::word_navigation`'s
default-segmenter path. The native shim still delegates to pi's original when
`options.segment`/`options.isAtomicSegment` are supplied.

The generated `.d.ts` blocks (JSDoc included) and `.js` glue are byte-identical
to the pre-swap baseline. Verified: cargo build/fmt/clippy clean, codespell
clean, and pi's own `word-navigation.test.ts` oracle passes 19/19 against the
locally-built addon.
@zmaril
zmaril force-pushed the napi-fluessig/module5 branch from 18158ba to e549210 Compare July 21, 2026 22:31
@zmaril
zmaril enabled auto-merge July 21, 2026 22:34
@zmaril
zmaril disabled auto-merge July 21, 2026 22:35
@zmaril
zmaril enabled auto-merge July 21, 2026 22:35
@zmaril
zmaril merged commit f6a0003 into main Jul 21, 2026
13 checks passed
@zmaril
zmaril deleted the napi-fluessig/module5 branch July 21, 2026 22:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants