fix: --json empty-results contract, accent-insensitive company matching - #57
Merged
Conversation
DeibyGS
enabled auto-merge (squash)
August 13, 2026 16:08
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
force-pushed
the
fix/cc-doctor-search-cv-fixes
branch
from
August 13, 2026 16:09
261dbe0 to
49c62f3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Chain Context
Summary
Three commits continuing the live-audit process from PR #56. Fixed the
--jsonmode 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
applyr/commands/core.pysearch --jsoncommandapplyr/commands/analytics.pylist,pipeline,gaps,trends,plan, andsalarycommands (6 commands total)applyr/duplicates.pynormalize_company()SQLite scalar function for accent-insensitive company matching in duplicate detection and search filterstests/test_empty_results_json.pytests/test_duplicates.pytests/test_cli_routing.pyCommits
fix(json): empty results stay valid JSON in --json mode —
search --jsonwith 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 theif as_json:branch). Applied reference pattern fromcmd_gaps_list(already correct) to all affected commands.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 andsearch --company(added in PR fix: doctor, search, and CV fixes found auditing real job offers #56), because SQL comparison only lowercased, never stripped diacritics. Addednormalize_company()(NFKD decompose + strip combining marks) as a SQLite scalar function, applied to all company matching logic.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 revealscmd_gapsandcmd_trendsboth correctly return a list (not a dict).Verification
python3 -m pytest -q)Type of change
Breaking changes
None.
Agent
CC— branch:fix/cc-doctor-search-cv-fixesReview 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 theapplyrCLI. 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.