Skip to content

docs: add feature reference and fix three bugs - #169

Merged
cardmagic merged 1 commit into
masterfrom
docs/features-and-cli
Aug 15, 2026
Merged

docs: add feature reference and fix three bugs#169
cardmagic merged 1 commit into
masterfrom
docs/features-and-cli

Conversation

@cardmagic

Copy link
Copy Markdown
Owner

Blocks the 2.7.0 tag. The documentation audit found three real bugs, so the release is not yet clean.

Three bugs, found by running the examples

Every doc example is now executed. Three failed, and each traced to a defect in the gem rather than a badly worded sentence.

1. Marshal is broken on Bayes. marshal_dump omitted min_word_length, so a restored classifier crashed on its first use:

Marshal.load(Marshal.dump(classifier)).classify("pills")
# ArgumentError: comparison of Integer with nil failed

Bayes was the only classifier affected. LSI, TF-IDF, Logistic Regression, and kNN all round-trip correctly. A dump written by an older version still loads and takes the configured default.

2. LSI#highest_relative_content returned an Enumerator. The method ended in a bare .map, so callers got #<Enumerator: [...]:map> instead of the array its own type annotation promises.

3. LSI#highest_ranked_stems repeated a stem. It resolved each weight back to a position with arr.index(value), which returns the first match. Tied weights all mapped to the same index:

lsi.highest_ranked_stems("dog puppy canine bark fetch loyal", 3)
# was => [:dog, :dog, :dog]
# now => [:dog, :puppi, :canin]

The existing test_keyword_search still passes, because its fixture has no ties.

Three README examples that could not work

Logistic Regression never called fit, so the Quick Start raised NotFittedError.

keywords scored text before any fit. Unlike classifier -r, which pulls a pre-trained model, keywords ships none, so the first command a reader ran failed:

$ keywords "Ruby is a programming language"
Error: No model found; run 'keywords fit' first...

The section now fits first, then scores.

Incremental LSI used the default auto_rebuild, under which incremental mode never starts. Each add rebuilds at once, the index builds from the first two documents, and the next add measures vocabulary growth against that tiny baseline, trips the 20% threshold, and disables the mode permanently. build_index(force: true) cannot recover it. The corrected example uses auto_rebuild: false, which is verified to work and to stay enabled.

New: docs/

Eleven pages covering both commands, each classifier, persistence, streaming, and configuration, plus an index with guidance on which classifier to pick. They ship in the gem, so the README's relative links resolve for gem users too.

The audit also found public API with no documentation at all. Now covered: LSI#<<, add_batch, highest_relative_content, highest_ranked_stems, singular_values, and Bayes#append_category.

New: the audit is repeatable

test/docs/documentation_test.rb asserts every value the docs print, 46 examples. It runs inside rake test, so CI fails whenever the docs and the code drift apart.

.claude/skills/audit-docs holds the full procedure, plus check_docs.rb for what a unit test cannot cover: em and en dashes, relative links, link fragments, and documented executables. I verified the checker by seeding three faults and confirming it reports all three and exits 1.

.gitignore narrows from .claude/ to .claude/* with a !.claude/skills/ exception, so the skill is tracked and local settings stay ignored.

Verification

  • 761 runs, 0 failures, 1 skip
  • RuboCop clean, 57 files, plus the skill script checked explicitly
  • rbs validate passes
  • static doc checks pass
  • no em or en dashes in any markdown
  • CLI examples in docs/cli.md and docs/keywords.md driven end to end against the gem installed into a throwaway GEM_HOME, in document order, from an empty directory

CHANGELOG.md records the three fixes under 2.7.0.

Audit the documentation by running every example. Three claims turned out
to be false, and each traced to a real bug rather than a wrong sentence.

Fix Marshal support in Bayes. marshal_dump left out min_word_length, so a
restored classifier raised "comparison of Integer with nil failed" on its
first classify. Bayes was the only classifier affected. A dump from an
older version still loads and takes the configured default.

Fix LSI#highest_relative_content, which ended in a bare .map and so
returned an Enumerator instead of the documented array.

Fix LSI#highest_ranked_stems, which looked up each weight with
arr.index(value). Tied weights all resolved to the same index, so one stem
repeated. It now ranks indices directly.

Correct the README where it contradicted the code:

- The logistic regression example never called fit, so it raised
  NotFittedError.
- The keywords examples scored text before any fit, but keywords ships no
  pre-trained model, so the first command a reader ran failed.
- The incremental LSI example used the default auto_rebuild, under which
  incremental mode never starts. Each add rebuilds immediately, the index
  builds from the first two documents, and the next add trips the
  vocabulary growth threshold and disables the mode for good.

Add docs/ covering both commands, each classifier, persistence,
streaming, and configuration, and ship it in the gem.

Add test/docs/documentation_test.rb, which asserts every published value.
rake test now fails when the docs and the code disagree.

Add a .claude/skills/audit-docs skill and a static checker for dashes,
relative links, link fragments, and documented executables. Track project
skills by narrowing the gitignore to .claude/* with an exception.
@greptile-apps

greptile-apps Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a packaged feature reference, executable documentation coverage, and a reusable static documentation audit. It also repairs Bayes Marshal restoration and two LSI result-selection defects.

  • Persists min_word_length in Bayes Marshal payloads while retaining legacy-payload compatibility.
  • Returns an array from LSI#highest_relative_content and preserves distinct indices when stem weights tie.
  • Adds classifier, CLI, persistence, streaming, and configuration reference pages to the gem.
  • Adds regression tests and executable assertions for documented examples.

Confidence Score: 5/5

The PR appears safe to merge with no actionable defects identified in the changed behavior.

The serialization compatibility paths and LSI ranking fixes are covered by focused regressions, and the documentation and packaging changes are consistent with their stated execution model.

Important Files Changed

Filename Overview
lib/classifier/bayes.rb Extends the Marshal payload with min_word_length and safely defaults legacy payloads that lack it.
lib/classifier/lsi.rb Corrects the relative-content return type and ranks tied stem weights by their distinct indices.
.claude/skills/audit-docs/check_docs.rb Adds a repository-root audit for prohibited dashes, unresolved Markdown links and fragments, and missing executables.
test/docs/documentation_test.rb Exercises documented Ruby examples and their printed contracts through the normal test task.
classifier.gemspec Includes the new Markdown reference pages in packaged gems.

Reviews (1): Last reviewed commit: "docs: add feature reference and fix thre..." | Re-trigger Greptile

@cardmagic cardmagic self-assigned this Aug 15, 2026
@cardmagic
cardmagic merged commit 643875a into master Aug 15, 2026
6 checks passed
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.

1 participant