Ruff rule-expansion evaluation
This record tracks the reviewed Ruff 0.16.1 rule expansion for AncestryLLM 0.6. Each batch starts from captured diagnostics, changes only the named rule families, and ends with focused regression evidence before the next batch begins. Ruff remains lock-resolved at 0.16.1 throughout the evaluation.
The expansion deliberately excludes ALL, docstring rules (D), blanket line
length enforcement (E501), and unsafe bulk fixes. CI selects GitHub annotation
output through RUFF_OUTPUT_FORMAT=github while continuing to call the
canonical make lint target.
Typing and import rules
The first batch enables TC and removes the redundant target-version =
"py312"; the project-wide requires-python floor remains the Python-version
authority. Ruff initially reported 274 findings:
| Rule | Findings | Disposition |
|---|---|---|
TC001 |
108 | Annotation-only application imports moved behind TYPE_CHECKING; runtime constructors, aliases, and factories remained eager. |
TC002 |
24 | Annotation-only third-party imports moved behind TYPE_CHECKING; Pydantic runtime evaluation remains explicitly characterized. |
TC003 |
112 | Annotation-only standard-library imports moved behind TYPE_CHECKING; runtime path, subprocess, and parser objects remained eager. |
TC004 |
2 | Imports needed at runtime moved out of typing-only blocks. |
TC006 |
28 | The selected safe quote fix was previewed, reviewed, and applied. |
| Total | 274 | The completed batch has zero Ruff findings and adds no suppression. |
The only global runtime-evaluation exception is
pydantic.BaseModel. Focused construction and JSON round-trip tests cover the
affected GenerationRequest and CapabilityManifest models so a typing import
cannot silently break model creation or serialization. Runtime aliases and
objects used by constructors were kept outside TYPE_CHECKING; no module-wide
or package-wide ignore was added.
Provider adapters continue to import optional SDKs only inside the call
boundary that uses them. A static AST contract rejects module-scope imports of
anthropic, google.genai, ollama, or openai. Isolated subprocess tests
also create every built-in provider selection and import the CLI while proving
that none of those SDK modules is eagerly loaded. This preserves a network-free
provider=none startup and prevents unrelated providers from inheriting an
SDK import cost.
Startup evidence
scripts/benchmark_cli_import.py performs isolated cold imports and emits a
sanitized schema-v1 JSON record. It reports only the Python version, bounded
timing samples, and any provider SDK module names; it contains no environment
values, paths, credentials, provider responses, or genealogy data.
The before and after measurements used Python 3.14.6 on macOS ARM64 with 15 fresh processes each:
| Measurement | Before | After | Change |
|---|---|---|---|
| Median CLI import | 703.294 ms | 705.395 ms | +2.101 ms (+0.3%) |
| Provider SDK modules loaded | 0 | 0 | No change |
The median change is below both the existing 100 ms absolute and 10% relative characterization thresholds, so it is not a material regression. The before run contained one 2,490.432 ms outlier; acceptance is based on the median, as defined by the existing characterization policy.
Regression evidence
- Configured Ruff check and format check pass across all configured repository paths.
- Strict mypy passes all 133 source files.
- The focused provider, Pydantic, workflow, and benchmark contract has six passing tests.
- All 1,020 tests in Python test modules changed by this batch pass.
- The focused GEDCOM suite passes 343 tests with three expected skips.
- The complete core-contract capture passes all 51 nodes across GEDCOM,
CLI/REPL, provider consent/offline behavior, incremental recovery, and
immutable RootsMagic groups. Its semantic digest is
d4394e3eb52dba6b0302ad40d87e411981b0da35b715aff4ff885f82ce61799e.
The capture report was written outside the repository, as required by the characterization runner. Timing values characterize this one development host; the supported Python 3.12-3.14 CI matrix remains authoritative.
Performance and modernization rules
The second batch enables PERF, C4, and FURB. Ruff initially reported 35
findings:
| Rule | Findings | Disposition |
|---|---|---|
PERF401 |
18 | Append-only loops became generator-backed list.extend calls after review of source order, filtering, partial mutation, and exception timing. |
C420 |
8 | Constant-valued comprehensions became dict.fromkeys only where the value is immutable; explicitly sorted key insertion remained sorted. |
FURB162 |
6 | ISO timestamp parsing now relies on Python 3.12’s native trailing-Z support, with existing UTC, offset, naive, and malformed-input tests retained. |
FURB192 |
2 | Nonempty string sets use min instead of sorting the complete set before selecting the first lexical value. |
C408 |
1 | One test-only dict constructor became an equivalent literal. |
| Total | 35 | The completed batch has zero Ruff findings and adds no suppression. |
No unsafe fix was applied. The complete proposed diff was previewed with Ruff, then each finding was changed by hand. GEDCOM finding collection, alternate-name serialization, RootsMagic continuation and family-member output, release-gate ordering, and architecture/audit violation ordering remain deterministic. The generator-backed extensions consume each source once and preserve incremental list population if construction raises.
Regression evidence
- The focused release, desktop, GEDCOM, sync/recovery, RootsMagic, architecture, characterization, benchmark, and Semgrep suites pass all 758 tests.
- Strict mypy passes all 133 source files, and configured Ruff check passes the complete repository.
- The complete core-contract capture again passes all 51 nodes. Its semantic
digest remains
d4394e3eb52dba6b0302ad40d87e411981b0da35b715aff4ff885f82ce61799e, exactly matching the typing-batch capture.
This is a static-policy and semantics-preserving modernization batch, so no new behavioral red test applies. The Ruff configuration contract was observed failing before the rule families were selected; existing acceptance, rejection, adversarial, and characterization tests then guarded the potentially observable transformations.
Language and correctness rules
The third batch enables UP, SIM, RET, PTH, DTZ, LOG, and ASYNC.
Ruff initially reported 240 findings:
| Family | Findings | Disposition |
|---|---|---|
UP |
90 | Modern annotations, aliases, enums, and standard-library APIs were reviewed against the Python 3.12 floor; one callable runtime alias remains explicit. |
SIM |
70 | Nested context managers and exception-only cleanup blocks were simplified without changing cleanup ownership or cancellation propagation. |
PTH |
73 | Ordinary filesystem calls use pathlib; eight lexical absolute-path calls and one descriptor enumeration remain narrowly suppressed at security boundaries. |
DTZ |
5 | Instant timestamps use aware UTC values; two timezone-free GEDCOM calendar operations and one fixed-time test fixture remain explicit exceptions. |
RET |
2 | Redundant branches were removed only where the preceding branch returns unconditionally. |
LOG |
0 | Enabled as reviewed policy coverage; the existing redacted logging paths already comply. |
ASYNC |
0 | Enabled as reviewed policy coverage; existing cancellation boundaries already comply. |
| Total | 240 | The completed batch has zero Ruff findings and no broad suppression. |
No unsafe bulk fix was applied. Safe-fix proposals were previewed by individual
rule, reviewed, and then applied selectively; exception-suppression conversions
were written by hand. One focused Windows marker rollback test caught a changed
mutual-exclusion branch during review. Restoring the original if/elif
relationship made the focused test pass before the wider batch was accepted.
The 17 line-level suppressions are enforced as an exact inventory by the Ruff expansion contract. They preserve behavior that the mechanical modernization must not alter:
- Bootstrap, credential snapshot, publication, and sync paths use lexical
absolute spellings until explicit symlink, reparse-point, or capability
checks complete. Replacing them with
Path.resolve()would follow a link too early and weaken the fail-closed check. - One sync cleanup enumerates an already-open directory descriptor. Reopening a
mutable pathname through
pathlibwould discard the held security capability. - GEDCOM dates are calendar values rather than instants, so the parser retains
timezone-free sentinel and
strptimevalues. Runtime and manifest timestamps remain timezone-aware UTC. ProgressEventremains a callable runtime alias. The publicErrorScalar,JSONValue,CommandScalar, andCommandValuealiases retain their prior runtime and Pydantic schema behavior instead of adopting PEP 695 semantics. The bootstrap receipt test retains its intentionally fixed naive fixture for the injected clock.
Regression evidence
- The focused cancellation, RootsMagic, file-ingress, publication, incremental-recovery, credential-snapshot, and verified-bootstrap suites pass all 790 tests.
- Strict mypy passes all 133 source files; configured Ruff check and format check pass the complete repository.
- The complete core-contract capture again passes all 51 nodes, and its semantic
digest remains
d4394e3eb52dba6b0302ad40d87e411981b0da35b715aff4ff885f82ce61799e.
The initial failing Ruff diagnostic set is the red observation for this static-policy change. Existing focused behavior and adversarial tests supplied the behavioral guardrails for path, rollback, GEDCOM, logging, and cancellation changes; the marker rollback regression was observed failing and fixed before the batch passed.
Architecture and security impact
These three batches change repository static-analysis policy and
semantics-preserving implementation details; they do not change an ancestry
API, CLI command registry, service DTO, provider contract, GEDCOM
representation, storage schema, FastAPI contract, or Electron boundary. The
transport-neutral architecture documented in ARCHITECTURE.md is therefore
unchanged. Runtime Pydantic and provider-import contracts guard the two typing
boundaries that could otherwise affect application startup, while the complete
core characterization guards the parser, writer, provider-consent, recovery,
and RootsMagic contracts.
No runtime dependency, network operation, executable trust root, privilege,
secret flow, or release-evidence schema is added. The verified uv bootstrap
and locked lint group remain the acquisition and execution boundary for
Ruff. The retained lexical-path and descriptor exceptions preserve existing
symlink, reparse-point, and race-resistant security checks rather than
broadening them. Consequently the threat inventory and control ownership in
THREAT_MODEL.md do not change. Existing controls remain intact: cloud calls
require explicit provider selection and consent, provider=none stays
network-free, RootsMagic sources remain immutable, and GEDCOM handling remains
loss-minimal.