Skip to content

core: fold the launcher argv config-path scan into Rust - #1106

Merged
thekevinscott merged 5 commits into
mainfrom
claude/1078-argv-config-paths-core
Sep 16, 2026
Merged

thekevinscott merged 5 commits into
mainfrom
claude/1078-argv-config-paths-core

Conversation

@thekevinbot

@thekevinbot thekevinbot commented Sep 9, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #1078

Part of the reduction epic #1072. Slice A of the issue: the argv config-path scan only. The rest of the extension-resolution cluster is #1111.

What this does

-c / --config was parsed three times: once by clap in the core (for real), and once each in the pip and npm launchers, which walk raw argv before the core runs because a config's [[dirsql.extension]] entry may name an extension by bare package name and only the interpreter can resolve that. Locating the config files is language-neutral, so the scan moves into the core as dirsql::launcher::config_paths_from_argv and both launchers reach it through their existing binding:

  • Rust: packages/rust/src/launcher.rs (#[doc(hidden)] pub mod launcher, 20 inline tests).
  • PyO3: dirsql._dirsql.config_paths_from_argv (packages/python/src/lib.rs, stub in _dirsql.pyi).
  • napi: the addon's configPathsFromArgv (packages/ts/napi/src/lib.rs, typed on CoreModule in core.ts).
  • Deleted: packages/python/dirsql/cli/config_paths_from_argv.py, packages/ts/src/cli/config-paths-from-argv.ts, and their unit tests.

The TS launcher still defers smol-toml behind the same existsSync guard (#720); the binding call replaces a pure function and reads no files.

Public API

Unchanged. The new function exists only on private surfaces: the underscore module dirsql._dirsql, the napi addon (not re-exported by index.ts), and a #[doc(hidden)] Rust module. dirsql/__init__.py and packages/ts/src/index.ts are untouched.

Tests

No assertion deleted or weakened. Both launcher unit suites already mocked the argv scan as a collaborator, so only the mock target moved (rce.config_paths_from_argv is now the builtin; TS mocks getCore()). Every case in the two deleted scanner test files has a counterpart in launcher.rs (the Python -c -c x → ["-c"] case is consumes_a_config_value_that_is_itself_a_config_flag), plus five cases neither had: --configuration, other long flags, --config= / -c= with an empty value, and -- not ending options. Colocated tests on the changed launcher sources pin the new seam (resolve_config_extensions_test.py, core.test.ts). Each binding crate has an inline forwarding test.

Red/green

  • RED 64178a2 pushed alone: launcher.rs with a stub returning the default. CI red for the right reason: rust Unit tests ran 15 of the 20 launcher::tests::* red at the assertion lines (https://github.com/thekevinscott/dirsql/actions/runs/34359934537/job/102494017762); the 5 that passed only assert the default. Integration tests, the coverage floor, rust mutation and both e2e-verify gates were red as consequences (stub, no receipts).
  • GREEN ae4f94c: implementation, both bindings, launcher rewiring, deletions, fragments, PARITY.
  • 8db47b0 / 04f5cca: e2e receipts. 1e17c94: colocated co-change tests the gate asked for, plus biome formatting.

Measured (epic srcline.py, source lines excluding tests, origin/main → head)

dir files src lines
packages/rust/src 27 → 28 6274 → 6294 (+20)
packages/python/src 1 → 1 381 → 388 (+7)
packages/ts/napi/src 1 → 1 777 → 781 (+4)
packages/python/dirsql 22 → 21 377 → 361 (−16)
packages/ts/src 26 → 25 502 → 484 (−18)
total 202 → 201 10070 → 10067 (−3)

Close to line-neutral, as the issue predicted for this slice; the case rests on the one-implementation principle, not the count.

Manual run

Built the testext fixture (dirsql_testext_answer() -> 42) and installed it by bare package name (dirsql_testext_pkg on PYTHONPATH; dirsql-testext-pkg under packages/ts/node_modules), with a config ext.toml naming it plus a files table. The compiled binary cannot resolve a package name, so a 42 proves the launcher found the config through the core scan.

$ dirsql query 'SELECT dirsql_testext_answer() AS a, (SELECT count(*) FROM files) AS n' -cext.toml        # also -c=ext.toml, --config=ext.toml
[{"a":42,"n":1}]
$ dirsql query 'SELECT dirsql_testext_answer() AS a'                                                      # no -c
dirsql query: SQLite error: no such function: dirsql_testext_answer ...   (exit 1)

Identical output from packages/python/.venv/bin/dirsql (maturin develop) and node packages/ts/dist/cli/dirsql.js (pnpm build) for all three spellings and the no-flag case.

E2E Verification

  • Ran e2e suites locally for every affected SDK
  • Python SDK e2e: pass
  • TypeScript SDK e2e: pass
  • Rust core e2e (if applicable): pass
  • packages/python/e2e-attestations/<branch>.json receipt written if packages/python changed (just e2e-attest-python)
  • packages/ts/e2e-attestations/<branch>.json receipt written if packages/ts changed (just e2e-attest-ts)
  • Command(s) run: just e2e-attest-python (runs just test-e2e), just e2e-attest-ts (runs pnpm test:e2e), cargo test --workspace --features cli, cargo bench -p dirsql, just preflight
  • Result summary: Python e2e 37 passed; TS e2e 16 passed (5 files); cargo test --workspace --features cli all green (843 lib tests + integration/e2e binaries, 0 failed); just preflight 0 failing pairs, 3 packaging SKIPs (need a built artifact); rust mutation gate 6 mutants tested, 0 survived; cargo bench -p dirsql exit 0, 33 benchmarks (no stored baseline to compare against; launcher.rs is on no benchmarked path)

Changelog / Migrations

  • Changelog fragment added under <root>/<pkg>/changelog.d/ for each changed package (or: skip-changelog trailer on a commit with reason)
  • Migration fragment added under <root>/<pkg>/migrations.d/ (or: not required -- additive/bugfix only)

Fragments: packages/rust/changelog.d/2026-09-09-launcher-config-path-scan.md (Added), packages/python/changelog.d/2026-09-09-launcher-config-path-scan.md (Changed), packages/ts/changelog.d/2026-09-09-launcher-config-path-scan.md (Changed). No migration fragment: runtime behavior is identical (same flags, same paths, same ./.dirsql.toml default) and no public API changes.

Parity

PARITY.md "Config-flag scanning breadth" updated. No drift introduced: both launchers now share one scan by construction.

Left alone

Adds dirsql::launcher::config_paths_from_argv with the 20 cases the three
launcher copies of this scan collectively cover, against a stub that always
returns the default config path. 15 of the 20 fail.

Refs #1078
…s through it

`dirsql::launcher::config_paths_from_argv` now walks argv for every
`--config` / `-c` value (all five spellings) and falls back to
`./.dirsql.toml`, turning the 20 red inline tests green.

Both bindings export it: `dirsql._dirsql.config_paths_from_argv` (PyO3)
and the napi addon's `configPathsFromArgv`. The pip and npm launchers
call through the binding, and the per-language copies of the scan are
deleted along with their unit tests. Launcher unit tests keep mocking the
scan at the same seam, now the binding.

PARITY.md records that the scan is shared by construction; changelog
fragments for python and ts describe the no-observable-change move.

Refs #1078, #1072
The launcher sources changed their collaborator from a sibling module to
the core binding, so the colocated tests now assert that seam: the Python
launcher's `config_paths_from_argv` is the `dirsql._dirsql` builtin, and
`CoreModule` carries `configPathsFromArgv` as a typed member.

Also biome-formats the two TS files the rewiring touched.
@thekevinscott
thekevinscott merged commit 7e849ea into main Sep 16, 2026
77 checks passed
@thekevinscott
thekevinscott deleted the claude/1078-argv-config-paths-core branch September 16, 2026 21:24
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.

core: fold the language-agnostic half of extension resolution into Rust (three argv parsers today)

2 participants