Skip to content

fix: --json empty-results contract, accent-insensitive company matching - #57

Merged
DeibyGS merged 3 commits into
mainfrom
fix/cc-doctor-search-cv-fixes
Aug 13, 2026
Merged

fix: --json empty-results contract, accent-insensitive company matching#57
DeibyGS merged 3 commits into
mainfrom
fix/cc-doctor-search-cv-fixes

Conversation

@DeibyGS

@DeibyGS DeibyGS commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Chain Context

Summary

Three commits continuing the live-audit process from PR #56. Fixed the --json mode contract across seven commands (empty results were printing human text instead of valid JSON), implemented accent-insensitive company matching to catch duplicates like "Mática Partners" vs "Matica Partners", and fixed two pre-existing test assertion bugs that were masked by the JSON contract violation.

Changes

File Type Description
applyr/commands/core.py 🐛 modified Fixed empty-results JSON contract in search --json command
applyr/commands/analytics.py 🐛 modified Fixed empty-results JSON contract in list, pipeline, gaps, trends, plan, and salary commands (6 commands total)
applyr/duplicates.py ✨ enhanced Added normalize_company() SQLite scalar function for accent-insensitive company matching in duplicate detection and search filters
tests/test_empty_results_json.py ✨ added New comprehensive test suite for --json empty-results contract across all affected commands
tests/test_duplicates.py 🐛 modified Extended company-matching tests to cover accent-insensitive scenarios
tests/test_cli_routing.py 🐛 modified Fixed contradictory/collision-prone assertions in new test cases

Commits

  1. fix(json): empty results stay valid JSON in --json modesearch --json with zero matches printed a human sentence instead of JSON, breaking the "--json is always parseable" contract every agent-facing command promises. Audited 7 commands total (search, list, pipeline, gaps, trends, plan, salary) — all had the same bug pattern (early return for human output before reaching the if as_json: branch). Applied reference pattern from cmd_gaps_list (already correct) to all affected commands.

  2. fix(duplicates): ignore accent differences in company matching — Found live: "Mática Partners" and "Matica Partners" (same company, inconsistent accent) were treated as separate entities by both add's duplicate warning and search --company (added in PR fix: doctor, search, and CV fixes found auditing real job offers #56), because SQL comparison only lowercased, never stripped diacritics. Added normalize_company() (NFKD decompose + strip combining marks) as a SQLite scalar function, applied to all company matching logic.

  3. test: fix wrong dict assumption in gaps/trends --json tests — Two pre-existing tests asserted isinstance(data, dict) inside a try/except that silently swallowed JSONDecodeError. Before the empty-results fix above, an empty DB made these commands print plain text so the assertion never actually ran. Now that --json stays valid JSON when empty, the assertion runs for real and reveals cmd_gaps and cmd_trends both correctly return a list (not a dict).

Verification

  • Tests: 576/576 ✅ (locally verified with python3 -m pytest -q)
  • Manual testing: verified empty-results --json output on all 7 affected commands
  • Accent-matching: tested "Mática Partners" vs "Matica Partners" deduplication
  • Budget: 182 insertions + 27 deletions = 209 lines (under 500) ✅

Type of change

  • 🐛 Bug fix
  • ✨ New feature (normalize_company scalar function)
  • ♻️ Refactor / cleanup
  • 🎨 UI / Visual
  • 🔧 Config / Infra / Chore
  • ⚡ Performance
  • 📝 Documentation

Breaking changes

None.

Agent

CC — branch: fix/cc-doctor-search-cv-fixes

Review notes

This is PR 2/2 of a deliberately chained set (split from a larger audit via /chained-pr). The two bugs fixed here (empty-results JSON contract, accent-insensitive matching) were discovered via live testing of PR #56's additions to the applyr CLI. The test fixes address assertion bugs that were masked by the empty-results contract violation and are now visible once that contract is enforced. No user-facing API changes — all fixes are internal correctness improvements to the --json contract and duplicate-detection heuristics.

@DeibyGS
DeibyGS enabled auto-merge (squash) August 13, 2026 16:08
DeibyGS and others added 3 commits August 13, 2026 18:09
Found while auditing applyr against real offers: `search --json` with
zero matches printed "No offers found matching '...'." — human text on
stdout in JSON mode, breaking the "--json is always parseable" contract
every agent-facing command promises in AGENT_INSTRUCTIONS.md.

Checking for the same shape turned up the identical bug in 6 more
commands (list, pipeline, gaps, trends, plan, salary) — all reached
their `if as_json:` branch only when rows existed, so an empty result
always fell through to the human-only early return regardless of
--json. cmd_gaps_list already handled this correctly
(`{"total": 0, "gaps": []}` even when empty) and served as the
reference pattern applied to the rest.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Found via a real offer during a live audit: "Mática Partners" and
"Matica Partners" (same company, inconsistent accent between postings)
were treated as two different companies by both add's duplicate
warning and search --company, because the SQL comparison only
lowercased, never stripped diacritics. Company history got silently
split across two spellings.

Added normalize_company() (NFKD decompose + strip combining marks),
registered as a SQLite scalar function so find_exact() and
find_company_offers() can compare accent-insensitively in SQL.
Diacritic-stripping carries none of the false-positive risk this
module's docstring already rejects fuzzy/substring matching for — it
can only make two spellings of the SAME company converge, never merge
two different companies.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Both tests asserted isinstance(data, dict) inside a try/except that
silently swallowed JSONDecodeError — before today's empty-results fix,
an empty DB made these commands print plain text, so json.loads()
always raised and the dict assertion never actually ran. Now that
--json stays valid JSON when empty, the assertion runs for real and
fails: cmd_gaps and cmd_trends both return a list, not a dict — that
was always the correct shape for their non-empty case too.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@DeibyGS
DeibyGS force-pushed the fix/cc-doctor-search-cv-fixes branch from 261dbe0 to 49c62f3 Compare August 13, 2026 16:09
@DeibyGS
DeibyGS merged commit 93389f0 into main Aug 13, 2026
12 checks passed
@DeibyGS
DeibyGS deleted the fix/cc-doctor-search-cv-fixes branch August 13, 2026 16:10
@DeibyGS DeibyGS mentioned this pull request Aug 13, 2026
7 tasks
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