* fix(extractors): resolve 3 silent contract mis-resolution bugs (#793)
Addresses Codex adversarial review findings for extractor contract
resolution on the new group extractor surface.
F1 (manifest-extractor): resolveSymbol passed the full "METHOD::path"
contract string through normalizeRoutePath, producing "/GET::/api/orders"
which never matches Route.name. Adds parseHttpContract() helper that
strips the METHOD:: prefix before path normalization. Contract ID
construction (buildContractId) is unchanged.
F2 (http-route-extractor): graph-assisted backfill used path-only
detections.find(), so multi-verb same-URL files attached the wrong
verb/handler to provider rows and inferred the wrong verb on FETCHES
consumer edges. Now requires path+method match when method is known,
and skips backfill when method is unknown and multiple detections tie
on path.
F3 (grpc-extractor): resolveProtoConflict seeded bestScore=-1 and only
replaced on strict >, so all-zero-score ties silently selected
candidates[0]. Now computes all scores, counts ties at the top score,
and returns null on ambiguity (caller skips contract emission and
warns with service name + candidate paths).
All three fixes are test-first; 73 tests pass across the three suites.
No schema changes, no new dependencies, contract ID wire format
(http::METHOD::path, grpc::pkg.Service/Method, http::*::path) preserved.
* fix(extractors): address PR #817 review — ambiguous symbol pick + contract id casing
Copilot + Claude review on PR #817 flagged two follow-up bugs on top of
the F1/F2/F3 fixes:
1. http-route-extractor: ambiguous multi-verb case left handlerName null
but still ran the CONTAINS DB query. pickSymbolUid(syms, null) then
silently picked pool[0] — reintroducing handler mis-attribution via
a different route than the .find() bug F2 fixed. Now gates symbol
enrichment on an ambiguousCandidates flag so the file-basename
fallback wins instead.
2. manifest-extractor: buildContractId passed raw user casing through
for the explicit-method form, so get::/api/orders and
GET::/api/orders produced different contract ids even though
parseHttpContract upper-cases during lookup. Now reuses
parseHttpContract + normalizeRoutePath to canonicalize both method
and path, so logically equivalent manifest inputs share a contract
id (and share a manifestSymbolUid fallback).
Adds one regression test per bug: lowercase vs uppercase manifest
contract ids must match, and ambiguous multi-verb with CONTAINS rows
must not silently attach a real handler or call the CONTAINS query
at all. 75 tests pass across the three extractor suites.
* chore: prettier formatting