Inflected words resolve: the Deinflector and Candidate ranking #11

Closed
opened 2026-08-10 09:26:36 +00:00 by aiko · 1 comment
Owner

Parent

#1

What to build

Tapping 食べさせられなかった finds 食べる.

The deinflection rule table is lifted from 10ten Japanese Reader (src/background/deinflect.ts, GPL-3.0) along with its existing test suite, rather than written fresh (ADR 0002). Attribution stays with the code and the licence stays GPL-3.0.

From the tap point the Lookup takes the text forward to a 16-character cap and shortens it one character at a time, running every substring through the deinflector. Each result is a Candidate: a Headword, the Reason Chain that reached it, and the length of text it consumed. All Candidates from one tap go to the plugin in a single request.

Two things then happen in the browser, and neither belongs on the server (ADR 0006):

  • Word-class filtering. A deinflection is only valid if the returned Entry's part of speech agrees with what the rule produced. 食べさせられなかった deinflects to 食べる only if 食べる is an ichidan verb. Candidates whose word class the Entry contradicts are dropped, not ranked.
  • Ranking by transformed length, not by raw match length (ADR 0005). This is the part that does the real work: a long inflected form that deinflects to a real word beats a short accidental match, which is why tapping the first character of 食べさせられなかった gives 食べる rather than 食.

The winning Entry appears in the sheet with its Reason Chain shown, so the reader can see which inflections were undone and in what order.

The 16-character cap is Yomitan's number, taken rather than invented (ADR 0005).

Acceptance criteria

  • The 10ten deinflection table and its tests are in the repo, with attribution intact
  • Tapping the first character of an inflected form resolves to its Headword
  • The scan stops at 16 characters from the tap point
  • Candidates carry their Reason Chain and the length of text they consumed
  • One tap produces one request, not one request per substring
  • A Candidate whose deinflection word class disagrees with the Entry's part of speech is dropped
  • Ranking is by transformed-text length; a case that distinguishes it from raw-match ranking is covered by a test
  • The Reason Chain is visible in the sheet

Blocked by

## Parent #1 ## What to build Tapping 食べさせられなかった finds 食べる. The deinflection rule table is lifted from 10ten Japanese Reader (`src/background/deinflect.ts`, GPL-3.0) along with its existing test suite, rather than written fresh (ADR 0002). Attribution stays with the code and the licence stays GPL-3.0. From the tap point the Lookup takes the text forward to a 16-character cap and shortens it one character at a time, running every substring through the deinflector. Each result is a Candidate: a Headword, the Reason Chain that reached it, and the length of text it consumed. All Candidates from one tap go to the plugin in a single request. Two things then happen in the browser, and neither belongs on the server (ADR 0006): - **Word-class filtering.** A deinflection is only valid if the returned Entry's part of speech agrees with what the rule produced. 食べさせられなかった deinflects to 食べる only if 食べる is an ichidan verb. Candidates whose word class the Entry contradicts are dropped, not ranked. - **Ranking by transformed length**, not by raw match length (ADR 0005). This is the part that does the real work: a long inflected form that deinflects to a real word beats a short accidental match, which is why tapping the first character of 食べさせられなかった gives 食べる rather than 食. The winning Entry appears in the sheet with its Reason Chain shown, so the reader can see which inflections were undone and in what order. The 16-character cap is Yomitan's number, taken rather than invented (ADR 0005). ## Acceptance criteria - [ ] The 10ten deinflection table and its tests are in the repo, with attribution intact - [ ] Tapping the first character of an inflected form resolves to its Headword - [ ] The scan stops at 16 characters from the tap point - [ ] Candidates carry their Reason Chain and the length of text they consumed - [ ] One tap produces one request, not one request per substring - [ ] A Candidate whose deinflection word class disagrees with the Entry's part of speech is dropped - [ ] Ranking is by transformed-text length; a case that distinguishes it from raw-match ranking is covered by a test - [ ] The Reason Chain is visible in the sheet ## Blocked by - #9
Author
Owner

This was generated by AI during triage.

Merged to main in 168b131.

The 10ten deinflection table and its test suite were lifted, not written: 628 of 629 lines of deinflect.ts and 561 of 562 of its tests are byte-identical to upstream, verified by re-downloading and diffing. Attribution intact, GPL-3.0 unchanged.

16-character scan from the tap point, Candidates carrying Reason Chain and consumed length, one batched request per tap, word-class filtering that drops Candidates the Entry contradicts, and ranking by consumed length rather than matched-Headword length. The Reason Chain is rendered in the sheet.

The ranking test was verified to go red when the comparison is swapped to Headword length — a suite that passes under both rules would not have tested the thing that matters.

One thing ADR 0002 did not anticipate: the lifted file imports @birchill/normal-jp, which publishes no licence anywhere — no LICENSE in its repo, no license field on npm (independently confirmed). Under ADR 0004 it could not be adopted, so web/src/kana.ts supplies its one function in ~12 lines with its own tests. Recorded as a Consequences bullet on ADR 0002.

> *This was generated by AI during triage.* Merged to `main` in 168b131. The 10ten deinflection table and its test suite were lifted, not written: **628 of 629 lines of `deinflect.ts` and 561 of 562 of its tests are byte-identical to upstream**, verified by re-downloading and diffing. Attribution intact, GPL-3.0 unchanged. 16-character scan from the tap point, Candidates carrying Reason Chain and consumed length, one batched request per tap, word-class filtering that drops Candidates the Entry contradicts, and ranking by consumed length rather than matched-Headword length. The Reason Chain is rendered in the sheet. The ranking test was verified to go **red** when the comparison is swapped to Headword length — a suite that passes under both rules would not have tested the thing that matters. **One thing ADR 0002 did not anticipate:** the lifted file imports `@birchill/normal-jp`, which publishes no licence anywhere — no `LICENSE` in its repo, no `license` field on npm (independently confirmed). Under ADR 0004 it could not be adopted, so `web/src/kana.ts` supplies its one function in ~12 lines with its own tests. Recorded as a Consequences bullet on ADR 0002.
aiko closed this issue 2026-08-10 10:59:38 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
mobrule/kurageyomi#11
No description provided.