Skip to content

fix(cli): skip DB probe when nested subcommand requests --help - #726

Open
syf2211 wants to merge 2 commits into
Metabuilder-Labs:mainfrom
syf2211:fix/group-subcommand-help-no-db
Open

fix(cli): skip DB probe when nested subcommand requests --help#726
syf2211 wants to merge 2 commits into
Metabuilder-Labs:mainfrom
syf2211:fix/group-subcommand-help-no-db

Conversation

@syf2211

@syf2211 syf2211 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Skip the DuckDB probe in the root CLI callback when the invocation requests --help, so nested commands like tj optimize downsize --help work while tj serve holds the write lock.

Motivation

Fixes #580. Running --help on a group subcommand failed with "Database is locked" whenever the daemon was running, because open_db() ran before Click rendered help.

Changes

  • Detect help tokens in unconsumed ctx.args during TjCommand.invoke and set ctx.obj["_skip_db_for_help"].
  • Skip open_db() in the root cli() callback when that flag is set (same path as no_db_commands).
  • Add parametrized integration tests for optimize downsize, backfill langfuse, and loop annotate.

Tests

pytest tests/integration/test_cli.py::test_nested_subcommand_help_skips_db_probe -v
ruff check tokenjam/cli/main.py tokenjam/cli/tj_status.py tests/integration/test_cli.py
mypy tokenjam/cli/main.py tokenjam/cli/tj_status.py

All passed.

Notes

  • composer-2.5 review: APPROVE
  • Normal invocations without --help still probe the DB as before.
  • Also fixes top-level tj <cmd> --help under the same lock condition.

Detect --help in remaining Click args during TjGroup/TjCommand invoke
and set a context flag so the root callback skips open_db(). Fixes
nested invocations like `tj optimize downsize --help` failing with a
database-lock error while tj serve is running.

Fixes Metabuilder-Labs#580.
@syf2211
syf2211 requested a review from anilmurty as a code owner August 17, 2026 00:09
@greptile-apps

greptile-apps Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR prevents help-only CLI invocations from probing DuckDB, allowing nested command help to render while the daemon holds the database lock.

  • Detects unconsumed help tokens before root command dispatch.
  • Reuses the no-database initialization path for help requests.
  • Adds integration coverage for nested optimize, backfill, and loop help.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
tokenjam/cli/tj_status.py Detects inherited help-option tokens during command invocation and records the help-only state in the shared Click context.
tokenjam/cli/main.py Routes help-only invocations through the existing no-database initialization path.
tests/integration/test_cli.py Verifies representative nested help invocations complete without calling open_db.

Sequence Diagram

sequenceDiagram
    participant User
    participant Invoke as TjCommand.invoke
    participant Root as cli callback
    participant Click
    User->>Invoke: tj group command --help
    Invoke->>Invoke: Detect help token
    Invoke->>Root: Set shared skip-DB marker
    Root->>Root: Load config without open_db()
    Root->>Click: Continue command dispatch
    Click-->>User: Render nested command help
Loading

Reviews (2): Last reviewed commit: "Merge branch 'main' into fix/group-subco..." | Re-trigger Greptile

@anilmurty anilmurty left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. I reproduced the original failure against a genuinely held DuckDB write lock (second process on an r/w connection, [api] port pointed at a dead port so the API fallback couldn't mask it) and confirmed the fix across the tree.

It fixes more than the issue claimed. #580 says top-level tj <group> --help is unaffected — it wasn't. tj cost --help and tj optimize --help were broken too. So this covers --help for every command not already in no_db_commands, at any depth. The no_db_commands set and the eager tj --help / --version paths are unchanged, as expected.

The seam is right: cli is a TjGroup, which resolves invoke to TjCommand.invoke, so the hook runs before the root callback fires and before the sub-context is built — the only point where the flag can be set in time. There's no separate TjGroup.invoke needing the same patch.

The detail worth calling out: reading ctx.help_option_names off the context chain rather than hardcoding "--help". -h isn't wired in this app today, and this picks it up for free the day someone adds context_settings. That's the kind of choice that stops a fix rotting.

Tests are load-bearing — reverting only the two source hunks fails all three parametrized cases — and they use the open_db(side_effect=AssertionError) pattern CLAUDE.md prescribes.

Two nits, non-blocking:

  1. The scan can trip on a token Click will consume as an option value. tj loop annotate SID --note --help sets the skip flag, runs with db=None, and dies with AttributeError: 'NoneType' object has no attribute 'execute'. It needs the token to be exactly --help, and I checked that no command in the tree uses ignore_unknown_options/allow_extra_args, so there's no pass-through command legitimately forwarding a --help to a subprocess — which was my main worry and it's clean. Nonsense input, nothing written on the crash path. Filing the general form as a follow-up rather than holding this.
  2. Setting the flag unconditionally (ctx.obj["_skip_db_for_help"] = self._tokens_request_help(ctx)) rather than only on True would make it self-clearing if a long-lived process ever reused one obj dict. Safe, since the leaf's invoke runs after the root callback has read it.

Test gaps worth a line each if you're already in here: nothing asserts the inverse — that a non-help invocation still opens the DB, which is the direction that would actually break the product — and nothing pins the newly-fixed single-level shape (tj cost --help), which is the broader win.

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.

tj <group> <subcommand> --help fails with a database-lock error while tj serve is running

2 participants