Version: 0.3.0, installed as a plugin (claude plugin install fluent@m98)
Two findings, both rooted in the same env-var/path-resolution layer.
1. Session results have no resolvable location
fluent_paths.py resolves the data directory properly — FLUENT_DATA_DIR, then $CLAUDE_PROJECT_DIR/data, then ./data, then ~/.claude/fluent-data — and exposes data_dir(), plugin_root() and backups_dir().
There is no results_dir() and no FLUENT_RESULTS_DIR. Instead, six skills hardcode a bare, root-anchored path:
fluent-learn/SKILL.md:150 — Save exchange to /results/fluent-learn-session-{NNN}.md
fluent-writing/SKILL.md:156, :219
fluent-reading/SKILL.md:205
fluent-review/SKILL.md:155
fluent-speaking/SKILL.md:168
fluent-session-analyzer/SKILL.md — reads /results/*.md
In plugin-install mode there is no repo root, so /results/ resolves against whatever the current working directory happens to be. For anyone running Claude Code from a directory that is not a fluent clone, session results land in an arbitrary place — in my case a knowledge vault's root, unrelated to where the six dbs live.
The read side makes it worse than misplaced files: fluent-session-analyzer looks in the same unresolved location. Redirect the write without redirecting the read and the analyzer finds nothing, then plans the next session from zero history — no error, just silently degraded tutoring.
Why a workaround isn't enough
FLUENT_DATA_DIR can move the dbs into a stable location, but nothing can move results/ short of editing the installed skills, which claude plugin update regenerates. The remaining options are a prose instruction telling the model where to write instead (fragile — it competes with the more specific Save exchange to /results/… line the skill itself supplies mid-session) or a filesystem symlink at the CWD root (invasive, and doesn't survive a clone on another machine).
Suggested fix
Add to fluent_paths.py, mirroring data_dir() exactly:
@lru_cache(maxsize=1)
def results_dir() -> Path:
"""Resolve the session-results directory (pure — does not create it)."""
env = os.environ.get("FLUENT_RESULTS_DIR")
if env:
return Path(env).expanduser().resolve()
return data_dir().parent / "results"
def ensure_results_dir() -> Path:
d = results_dir()
d.mkdir(parents=True, exist_ok=True)
return d
Defaulting to data_dir().parent / "results" keeps clone-mode behaviour identical (./data → ./results) while giving plugin-mode installs a location that tracks wherever the learner's data already lives. Then replace the six hardcoded /results/ strings with a reference to it, the same way the skills already resolve the data dir via the helper.
Happy to open a PR if the approach looks right.
2. The test suite fails whenever FLUENT_DATA_DIR is set
tests/test_update_db.py builds a fixture DB in a temp dir, but does not clear FLUENT_DATA_DIR from the subprocess environment. Since fluent_paths.data_dir() gives that variable top precedence, the scripts under test read the real data directory instead of the fixture.
$ FLUENT_DATA_DIR=/some/real/path python3 -m unittest discover -s tests
...
AssertionError: 2 != 0 : b"[Fluent] Error loading databases: [Errno 2] No such file or directory: '…/learner-profile.json'"
Ran 12 tests in 1.051s
FAILED (failures=11)
$ env -u FLUENT_DATA_DIR python3 -m unittest discover -s tests
Ran 12 tests in 1.429s
OK
11 of 12 fail. FLUENT_DATA_DIR is the variable the README tells users to export for multiple learners (export FLUENT_DATA_DIR=~/.fluent/dutch), so the suite is broken for exactly the audience following the documented setup — and it fails in CI-invisible fashion for anyone who has it in their shell profile.
Suggested fix
Scrub the resolution env vars in setUp, so tests are hermetic regardless of the developer's environment:
env = {k: v for k, v in os.environ.items()
if k not in ("FLUENT_DATA_DIR", "FLUENT_RESULTS_DIR", "CLAUDE_PROJECT_DIR")}
env["FLUENT_DATA_DIR"] = str(self.tmp_data) # point at the fixture explicitly
Explicitly pinning it at the fixture is better than merely unsetting it, since it also stops the tests from silently falling through to ~/.claude/fluent-data.
Environment
Windows 11, Python 3.13.13, Claude Code CLI, plugin installed at project scope. Otherwise the install validated cleanly end-to-end: read-db.py, update-db.py, validate-data.py, session-start.py and session-end.py all behaved correctly, and SM-2 produced textbook first-exposure values (easiness_factor 2.5, interval_days 1, repetitions 0, due_date = created + 1). Nice project — thanks for building it.
Version: 0.3.0, installed as a plugin (
claude plugin install fluent@m98)Two findings, both rooted in the same env-var/path-resolution layer.
1. Session results have no resolvable location
fluent_paths.pyresolves the data directory properly —FLUENT_DATA_DIR, then$CLAUDE_PROJECT_DIR/data, then./data, then~/.claude/fluent-data— and exposesdata_dir(),plugin_root()andbackups_dir().There is no
results_dir()and noFLUENT_RESULTS_DIR. Instead, six skills hardcode a bare, root-anchored path:fluent-learn/SKILL.md:150—Save exchange to /results/fluent-learn-session-{NNN}.mdfluent-writing/SKILL.md:156,:219fluent-reading/SKILL.md:205fluent-review/SKILL.md:155fluent-speaking/SKILL.md:168fluent-session-analyzer/SKILL.md— reads/results/*.mdIn plugin-install mode there is no repo root, so
/results/resolves against whatever the current working directory happens to be. For anyone running Claude Code from a directory that is not a fluent clone, session results land in an arbitrary place — in my case a knowledge vault's root, unrelated to where the six dbs live.The read side makes it worse than misplaced files:
fluent-session-analyzerlooks in the same unresolved location. Redirect the write without redirecting the read and the analyzer finds nothing, then plans the next session from zero history — no error, just silently degraded tutoring.Why a workaround isn't enough
FLUENT_DATA_DIRcan move the dbs into a stable location, but nothing can moveresults/short of editing the installed skills, whichclaude plugin updateregenerates. The remaining options are a prose instruction telling the model where to write instead (fragile — it competes with the more specificSave exchange to /results/…line the skill itself supplies mid-session) or a filesystem symlink at the CWD root (invasive, and doesn't survive a clone on another machine).Suggested fix
Add to
fluent_paths.py, mirroringdata_dir()exactly:Defaulting to
data_dir().parent / "results"keeps clone-mode behaviour identical (./data→./results) while giving plugin-mode installs a location that tracks wherever the learner's data already lives. Then replace the six hardcoded/results/strings with a reference to it, the same way the skills already resolve the data dir via the helper.Happy to open a PR if the approach looks right.
2. The test suite fails whenever
FLUENT_DATA_DIRis settests/test_update_db.pybuilds a fixture DB in a temp dir, but does not clearFLUENT_DATA_DIRfrom the subprocess environment. Sincefluent_paths.data_dir()gives that variable top precedence, the scripts under test read the real data directory instead of the fixture.11 of 12 fail.
FLUENT_DATA_DIRis the variable the README tells users to export for multiple learners (export FLUENT_DATA_DIR=~/.fluent/dutch), so the suite is broken for exactly the audience following the documented setup — and it fails in CI-invisible fashion for anyone who has it in their shell profile.Suggested fix
Scrub the resolution env vars in
setUp, so tests are hermetic regardless of the developer's environment:Explicitly pinning it at the fixture is better than merely unsetting it, since it also stops the tests from silently falling through to
~/.claude/fluent-data.Environment
Windows 11, Python 3.13.13, Claude Code CLI, plugin installed at project scope. Otherwise the install validated cleanly end-to-end:
read-db.py,update-db.py,validate-data.py,session-start.pyandsession-end.pyall behaved correctly, and SM-2 produced textbook first-exposure values (easiness_factor2.5,interval_days1,repetitions0,due_date= created + 1). Nice project — thanks for building it.