diff --git a/src/domain/graph/builder/stages/native-orchestrator.ts b/src/domain/graph/builder/stages/native-orchestrator.ts index a1bf7aa91..fbc860683 100644 --- a/src/domain/graph/builder/stages/native-orchestrator.ts +++ b/src/domain/graph/builder/stages/native-orchestrator.ts @@ -959,6 +959,14 @@ interface PostPassTimings { techniqueBackfillMs: number; } +interface PostPassTimings { + gapDetectMs: number; + chaMs: number; + thisDispatchMs: number; + reclassifyMs: number; + techniqueBackfillMs: number; +} + /** Format timing result from native orchestrator phases + JS post-processing. */ function formatNativeTimingResult( p: Record, diff --git a/tests/benchmarks/resolution/fixtures/elixir/expected-edges.json b/tests/benchmarks/resolution/fixtures/elixir/expected-edges.json index af1085cb9..d6782901c 100644 --- a/tests/benchmarks/resolution/fixtures/elixir/expected-edges.json +++ b/tests/benchmarks/resolution/fixtures/elixir/expected-edges.json @@ -4,148 +4,148 @@ "description": "Hand-annotated call edges for Elixir resolution benchmark", "edges": [ { - "source": { "name": "validate_user", "file": "validators.ex" }, - "target": { "name": "valid_name?", "file": "validators.ex" }, + "source": { "name": "Validators.validate_user", "file": "validators.ex" }, + "target": { "name": "Validators.valid_name?", "file": "validators.ex" }, "kind": "calls", "mode": "same-file", - "notes": "Same-module helper call within Validators" + "notes": "Same-module helper call within Validators — extractor emits module-qualified names" }, { - "source": { "name": "validate_user", "file": "validators.ex" }, - "target": { "name": "valid_email?", "file": "validators.ex" }, + "source": { "name": "Validators.validate_user", "file": "validators.ex" }, + "target": { "name": "Validators.valid_email?", "file": "validators.ex" }, "kind": "calls", "mode": "same-file", - "notes": "Same-module helper call within Validators" + "notes": "Same-module helper call within Validators — extractor emits module-qualified names" }, { - "source": { "name": "create_user", "file": "service.ex" }, - "target": { "name": "validate_user", "file": "validators.ex" }, + "source": { "name": "UserService.create_user", "file": "service.ex" }, + "target": { "name": "Validators.validate_user", "file": "validators.ex" }, "kind": "calls", "mode": "module-function", "notes": "Validators.validate_user() — cross-module qualified call" }, { - "source": { "name": "create_user", "file": "service.ex" }, - "target": { "name": "save", "file": "repository.ex" }, + "source": { "name": "UserService.create_user", "file": "service.ex" }, + "target": { "name": "UserRepository.save", "file": "repository.ex" }, "kind": "calls", "mode": "module-function", "notes": "UserRepository.save() — cross-module qualified call" }, { - "source": { "name": "get_user", "file": "service.ex" }, - "target": { "name": "find_by_id", "file": "repository.ex" }, + "source": { "name": "UserService.get_user", "file": "service.ex" }, + "target": { "name": "UserRepository.find_by_id", "file": "repository.ex" }, "kind": "calls", "mode": "module-function", "notes": "UserRepository.find_by_id() — cross-module qualified call" }, { - "source": { "name": "remove_user", "file": "service.ex" }, - "target": { "name": "delete", "file": "repository.ex" }, + "source": { "name": "UserService.remove_user", "file": "service.ex" }, + "target": { "name": "UserRepository.delete", "file": "repository.ex" }, "kind": "calls", "mode": "module-function", "notes": "UserRepository.delete() — cross-module qualified call" }, { - "source": { "name": "list_users", "file": "service.ex" }, - "target": { "name": "list_all", "file": "repository.ex" }, + "source": { "name": "UserService.list_users", "file": "service.ex" }, + "target": { "name": "UserRepository.list_all", "file": "repository.ex" }, "kind": "calls", "mode": "module-function", "notes": "UserRepository.list_all() — cross-module qualified call" }, { - "source": { "name": "display_user", "file": "service.ex" }, - "target": { "name": "get_user", "file": "service.ex" }, + "source": { "name": "UserService.display_user", "file": "service.ex" }, + "target": { "name": "UserService.get_user", "file": "service.ex" }, "kind": "calls", "mode": "same-file", - "notes": "Same-module call to get_user within UserService" + "notes": "Same-module call to get_user within UserService — extractor emits module-qualified names" }, { - "source": { "name": "display_user", "file": "service.ex" }, - "target": { "name": "format_user", "file": "service.ex" }, + "source": { "name": "UserService.display_user", "file": "service.ex" }, + "target": { "name": "UserService.format_user", "file": "service.ex" }, "kind": "calls", "mode": "same-file", - "notes": "Same-module call to private helper format_user" + "notes": "Same-module call to private helper format_user — extractor emits module-qualified names" }, { - "source": { "name": "run", "file": "main.ex" }, - "target": { "name": "new_store", "file": "repository.ex" }, + "source": { "name": "Main.run", "file": "main.ex" }, + "target": { "name": "UserRepository.new_store", "file": "repository.ex" }, "kind": "calls", "mode": "module-function", "notes": "UserRepository.new_store() — cross-module qualified call" }, { - "source": { "name": "run", "file": "main.ex" }, - "target": { "name": "create_user", "file": "service.ex" }, + "source": { "name": "Main.run", "file": "main.ex" }, + "target": { "name": "UserService.create_user", "file": "service.ex" }, "kind": "calls", "mode": "module-function", "notes": "UserService.create_user() — cross-module qualified call" }, { - "source": { "name": "run", "file": "main.ex" }, - "target": { "name": "get_user", "file": "service.ex" }, + "source": { "name": "Main.run", "file": "main.ex" }, + "target": { "name": "UserService.get_user", "file": "service.ex" }, "kind": "calls", "mode": "module-function", "notes": "UserService.get_user() — cross-module qualified call" }, { - "source": { "name": "run", "file": "main.ex" }, - "target": { "name": "list_users", "file": "service.ex" }, + "source": { "name": "Main.run", "file": "main.ex" }, + "target": { "name": "UserService.list_users", "file": "service.ex" }, "kind": "calls", "mode": "module-function", "notes": "UserService.list_users() — cross-module qualified call" }, { - "source": { "name": "run", "file": "main.ex" }, - "target": { "name": "display_user", "file": "service.ex" }, + "source": { "name": "Main.run", "file": "main.ex" }, + "target": { "name": "UserService.display_user", "file": "service.ex" }, "kind": "calls", "mode": "module-function", "notes": "UserService.display_user() — cross-module qualified call" }, { - "source": { "name": "run", "file": "main.ex" }, - "target": { "name": "remove_user", "file": "service.ex" }, + "source": { "name": "Main.run", "file": "main.ex" }, + "target": { "name": "UserService.remove_user", "file": "service.ex" }, "kind": "calls", "mode": "module-function", "notes": "UserService.remove_user() — cross-module qualified call" }, { - "source": { "name": "run", "file": "main.ex" }, - "target": { "name": "fetch", "file": "patterns.ex" }, + "source": { "name": "Main.run", "file": "main.ex" }, + "target": { "name": "Patterns.fetch", "file": "patterns.ex" }, "kind": "calls", "mode": "module-function", "notes": "Patterns.fetch() — exercises default-value parameter extraction" }, { - "source": { "name": "run", "file": "main.ex" }, - "target": { "name": "first_of", "file": "patterns.ex" }, + "source": { "name": "Main.run", "file": "main.ex" }, + "target": { "name": "Patterns.first_of", "file": "patterns.ex" }, "kind": "calls", "mode": "module-function", "notes": "Patterns.first_of() — exercises tuple-pattern parameter extraction" }, { - "source": { "name": "run", "file": "main.ex" }, - "target": { "name": "name_of", "file": "patterns.ex" }, + "source": { "name": "Main.run", "file": "main.ex" }, + "target": { "name": "Patterns.name_of", "file": "patterns.ex" }, "kind": "calls", "mode": "module-function", "notes": "Patterns.name_of() — exercises map-pattern parameter extraction" }, { - "source": { "name": "run", "file": "main.ex" }, - "target": { "name": "id_of", "file": "patterns.ex" }, + "source": { "name": "Main.run", "file": "main.ex" }, + "target": { "name": "Patterns.id_of", "file": "patterns.ex" }, "kind": "calls", "mode": "module-function", "notes": "Patterns.id_of() — exercises struct-pattern parameter extraction" }, { - "source": { "name": "run", "file": "main.ex" }, - "target": { "name": "head_of", "file": "patterns.ex" }, + "source": { "name": "Main.run", "file": "main.ex" }, + "target": { "name": "Patterns.head_of", "file": "patterns.ex" }, "kind": "calls", "mode": "module-function", "notes": "Patterns.head_of() — exercises list-cons pattern parameter extraction" }, { - "source": { "name": "run", "file": "main.ex" }, - "target": { "name": "all_of", "file": "patterns.ex" }, + "source": { "name": "Main.run", "file": "main.ex" }, + "target": { "name": "Patterns.all_of", "file": "patterns.ex" }, "kind": "calls", "mode": "module-function", "notes": "Patterns.all_of() — exercises list pattern parameter extraction" diff --git a/tests/benchmarks/resolution/fixtures/julia/expected-edges.json b/tests/benchmarks/resolution/fixtures/julia/expected-edges.json index 9d0661c74..df2f83baf 100644 --- a/tests/benchmarks/resolution/fixtures/julia/expected-edges.json +++ b/tests/benchmarks/resolution/fixtures/julia/expected-edges.json @@ -4,109 +4,109 @@ "description": "Hand-annotated call edges for Julia resolution benchmark", "edges": [ { - "source": { "name": "main", "file": "main.jl" }, - "target": { "name": "new_repo", "file": "repository.jl" }, + "source": { "name": "App.main", "file": "main.jl" }, + "target": { "name": "Repository.new_repo", "file": "repository.jl" }, "kind": "calls", "mode": "module-function", - "notes": "Repository.new_repo() — qualified call to Repository module" + "notes": "Repository.new_repo() — qualified call to Repository module; extractor emits module-qualified names" }, { - "source": { "name": "main", "file": "main.jl" }, - "target": { "name": "create_user", "file": "service.jl" }, + "source": { "name": "App.main", "file": "main.jl" }, + "target": { "name": "Service.create_user", "file": "service.jl" }, "kind": "calls", "mode": "module-function", - "notes": "Service.create_user() — qualified call to Service module" + "notes": "Service.create_user() — qualified call to Service module; extractor emits module-qualified names" }, { - "source": { "name": "main", "file": "main.jl" }, - "target": { "name": "get_user", "file": "service.jl" }, + "source": { "name": "App.main", "file": "main.jl" }, + "target": { "name": "Service.get_user", "file": "service.jl" }, "kind": "calls", "mode": "module-function", - "notes": "Service.get_user() — qualified call to Service module" + "notes": "Service.get_user() — qualified call to Service module; extractor emits module-qualified names" }, { - "source": { "name": "main", "file": "main.jl" }, - "target": { "name": "remove_user", "file": "service.jl" }, + "source": { "name": "App.main", "file": "main.jl" }, + "target": { "name": "Service.remove_user", "file": "service.jl" }, "kind": "calls", "mode": "module-function", - "notes": "Service.remove_user() — qualified call to Service module" + "notes": "Service.remove_user() — qualified call to Service module; extractor emits module-qualified names" }, { - "source": { "name": "main", "file": "main.jl" }, - "target": { "name": "summary", "file": "service.jl" }, + "source": { "name": "App.main", "file": "main.jl" }, + "target": { "name": "Service.summary", "file": "service.jl" }, "kind": "calls", "mode": "module-function", - "notes": "Service.summary() — qualified call to Service module" + "notes": "Service.summary() — qualified call to Service module; extractor emits module-qualified names" }, { - "source": { "name": "create_user", "file": "service.jl" }, - "target": { "name": "validate_name", "file": "validators.jl" }, + "source": { "name": "Service.create_user", "file": "service.jl" }, + "target": { "name": "Validators.validate_name", "file": "validators.jl" }, "kind": "calls", "mode": "module-function", - "notes": "Validators.validate_name() — qualified call to Validators module" + "notes": "Validators.validate_name() — qualified call to Validators module; extractor emits module-qualified names" }, { - "source": { "name": "create_user", "file": "service.jl" }, - "target": { "name": "validate_email", "file": "validators.jl" }, + "source": { "name": "Service.create_user", "file": "service.jl" }, + "target": { "name": "Validators.validate_email", "file": "validators.jl" }, "kind": "calls", "mode": "module-function", - "notes": "Validators.validate_email() — qualified call to Validators module" + "notes": "Validators.validate_email() — qualified call to Validators module; extractor emits module-qualified names" }, { - "source": { "name": "create_user", "file": "service.jl" }, - "target": { "name": "save", "file": "repository.jl" }, + "source": { "name": "Service.create_user", "file": "service.jl" }, + "target": { "name": "Repository.save", "file": "repository.jl" }, "kind": "calls", "mode": "module-function", - "notes": "Repository.save() — qualified call to Repository module" + "notes": "Repository.save() — qualified call to Repository module; extractor emits module-qualified names" }, { - "source": { "name": "get_user", "file": "service.jl" }, - "target": { "name": "find_by_id", "file": "repository.jl" }, + "source": { "name": "Service.get_user", "file": "service.jl" }, + "target": { "name": "Repository.find_by_id", "file": "repository.jl" }, "kind": "calls", "mode": "module-function", - "notes": "Repository.find_by_id() — qualified call to Repository module" + "notes": "Repository.find_by_id() — qualified call to Repository module; extractor emits module-qualified names" }, { - "source": { "name": "remove_user", "file": "service.jl" }, - "target": { "name": "delete", "file": "repository.jl" }, + "source": { "name": "Service.remove_user", "file": "service.jl" }, + "target": { "name": "Repository.delete", "file": "repository.jl" }, "kind": "calls", "mode": "module-function", - "notes": "Repository.delete() — qualified call to Repository module" + "notes": "Repository.delete() — qualified call to Repository module; extractor emits module-qualified names" }, { - "source": { "name": "summary", "file": "service.jl" }, - "target": { "name": "count", "file": "repository.jl" }, + "source": { "name": "Service.summary", "file": "service.jl" }, + "target": { "name": "Repository.count", "file": "repository.jl" }, "kind": "calls", "mode": "module-function", - "notes": "Repository.count() — qualified call to Repository module" + "notes": "Repository.count() — qualified call to Repository module; extractor emits module-qualified names" }, { - "source": { "name": "summary", "file": "service.jl" }, - "target": { "name": "format_summary", "file": "service.jl" }, + "source": { "name": "Service.summary", "file": "service.jl" }, + "target": { "name": "Service.format_summary", "file": "service.jl" }, "kind": "calls", "mode": "same-file", - "notes": "Same-file call to private helper function" + "notes": "Same-file call to private helper function — extractor emits module-qualified names" }, { - "source": { "name": "validate_name", "file": "validators.jl" }, - "target": { "name": "check_length", "file": "validators.jl" }, + "source": { "name": "Validators.validate_name", "file": "validators.jl" }, + "target": { "name": "Validators.check_length", "file": "validators.jl" }, "kind": "calls", "mode": "same-file", - "notes": "Same-file call to private helper function" + "notes": "Same-file call to private helper function — extractor emits module-qualified names" }, { - "source": { "name": "validate_email", "file": "validators.jl" }, - "target": { "name": "contains_at", "file": "validators.jl" }, + "source": { "name": "Validators.validate_email", "file": "validators.jl" }, + "target": { "name": "Validators.contains_at", "file": "validators.jl" }, "kind": "calls", "mode": "same-file", - "notes": "Same-file call to private helper function" + "notes": "Same-file call to private helper function — extractor emits module-qualified names" }, { - "source": { "name": "count", "file": "repository.jl" }, - "target": { "name": "count_entries", "file": "repository.jl" }, + "source": { "name": "Repository.count", "file": "repository.jl" }, + "target": { "name": "Repository.count_entries", "file": "repository.jl" }, "kind": "calls", "mode": "same-file", - "notes": "Same-file call to private helper function" + "notes": "Same-file call to private helper function — extractor emits module-qualified names" } ] } diff --git a/tests/benchmarks/resolution/fixtures/objc/expected-edges.json b/tests/benchmarks/resolution/fixtures/objc/expected-edges.json index 82ceb667f..a1db6bcd5 100644 --- a/tests/benchmarks/resolution/fixtures/objc/expected-edges.json +++ b/tests/benchmarks/resolution/fixtures/objc/expected-edges.json @@ -3,89 +3,96 @@ "language": "objc", "description": "Hand-annotated call edges for Objective-C resolution benchmark", "edges": [ + { + "source": { "name": "main", "file": "main.m" }, + "target": { "name": "run", "file": "main.m" }, + "kind": "calls", + "mode": "static", + "notes": "main() calls run() — plain C function call; extractor sees both as plain functions" + }, { "source": { "name": "run", "file": "main.m" }, - "target": { "name": "UserService.initWithRepository", "file": "Service.m" }, + "target": { "name": "UserService.initWithRepository:", "file": "Service.m" }, "kind": "calls", "mode": "constructor", - "notes": "[[UserService alloc] initWithRepository:repo] — message send constructor" + "notes": "[[UserService alloc] initWithRepository:repo] — message send constructor; full selector includes trailing colon" }, { "source": { "name": "run", "file": "main.m" }, - "target": { "name": "UserService.createUserWithId", "file": "Service.m" }, + "target": { "name": "UserService.createUserWithId:name:email:", "file": "Service.m" }, "kind": "calls", "mode": "receiver-typed", - "notes": "[svc createUserWithId:...] — message send on UserService instance" + "notes": "[svc createUserWithId:name:email:...] — message send on UserService instance; full multi-part selector" }, { "source": { "name": "run", "file": "main.m" }, - "target": { "name": "UserService.getUserWithId", "file": "Service.m" }, + "target": { "name": "UserService.getUserWithId:", "file": "Service.m" }, "kind": "calls", "mode": "receiver-typed", - "notes": "[svc getUserWithId:...] — message send on UserService instance" + "notes": "[svc getUserWithId:...] — message send on UserService instance; full selector includes trailing colon" }, { "source": { "name": "run", "file": "main.m" }, - "target": { "name": "UserService.removeUserWithId", "file": "Service.m" }, + "target": { "name": "UserService.removeUserWithId:", "file": "Service.m" }, "kind": "calls", "mode": "receiver-typed", - "notes": "[svc removeUserWithId:...] — message send on UserService instance" + "notes": "[svc removeUserWithId:...] — message send on UserService instance; full selector includes trailing colon" }, { "source": { "name": "run", "file": "main.m" }, - "target": { "name": "Validators.isValidEmail", "file": "Validators.m" }, + "target": { "name": "Validators.isValidEmail:", "file": "Validators.m" }, "kind": "calls", "mode": "static", - "notes": "[Validators isValidEmail:...] — class method message send" + "notes": "[Validators isValidEmail:...] — class method message send; full selector includes trailing colon" }, { - "source": { "name": "UserService.createUserWithId", "file": "Service.m" }, - "target": { "name": "Validators.isValidEmail", "file": "Validators.m" }, + "source": { "name": "UserService.createUserWithId:name:email:", "file": "Service.m" }, + "target": { "name": "Validators.isValidEmail:", "file": "Validators.m" }, "kind": "calls", "mode": "static", - "notes": "[Validators isValidEmail:...] — class method message send" + "notes": "[Validators isValidEmail:...] — class method message send; full selector includes trailing colon" }, { - "source": { "name": "UserService.createUserWithId", "file": "Service.m" }, - "target": { "name": "Validators.isValidName", "file": "Validators.m" }, + "source": { "name": "UserService.createUserWithId:name:email:", "file": "Service.m" }, + "target": { "name": "Validators.isValidName:", "file": "Validators.m" }, "kind": "calls", "mode": "static", - "notes": "[Validators isValidName:...] — class method message send" + "notes": "[Validators isValidName:...] — class method message send; full selector includes trailing colon" }, { - "source": { "name": "UserService.createUserWithId", "file": "Service.m" }, - "target": { "name": "UserRepository.saveWithId", "file": "Repository.m" }, + "source": { "name": "UserService.createUserWithId:name:email:", "file": "Service.m" }, + "target": { "name": "UserRepository.saveWithId:name:", "file": "Repository.m" }, "kind": "calls", "mode": "receiver-typed", - "notes": "[_repo saveWithId:...] — message send on UserRepository ivar" + "notes": "[_repo saveWithId:name:...] — message send on UserRepository ivar; full selector" }, { - "source": { "name": "UserService.getUserWithId", "file": "Service.m" }, - "target": { "name": "UserRepository.findById", "file": "Repository.m" }, + "source": { "name": "UserService.getUserWithId:", "file": "Service.m" }, + "target": { "name": "UserRepository.findById:", "file": "Repository.m" }, "kind": "calls", "mode": "receiver-typed", - "notes": "[_repo findById:...] — message send on UserRepository ivar" + "notes": "[_repo findById:...] — message send on UserRepository ivar; full selector includes trailing colon" }, { - "source": { "name": "UserService.removeUserWithId", "file": "Service.m" }, - "target": { "name": "UserRepository.deleteWithId", "file": "Repository.m" }, + "source": { "name": "UserService.removeUserWithId:", "file": "Service.m" }, + "target": { "name": "UserRepository.deleteWithId:", "file": "Repository.m" }, "kind": "calls", "mode": "receiver-typed", - "notes": "[_repo deleteWithId:...] — message send on UserRepository ivar" + "notes": "[_repo deleteWithId:...] — message send on UserRepository ivar; full selector includes trailing colon" }, { - "source": { "name": "Validators.validateUser", "file": "Validators.m" }, - "target": { "name": "Validators.isValidName", "file": "Validators.m" }, + "source": { "name": "Validators.validateUser:email:", "file": "Validators.m" }, + "target": { "name": "Validators.isValidName:", "file": "Validators.m" }, "kind": "calls", "mode": "same-file", - "notes": "[self isValidName:...] — same-class class method call" + "notes": "[self isValidName:...] — same-class class method call; full selector includes trailing colon" }, { - "source": { "name": "Validators.validateUser", "file": "Validators.m" }, - "target": { "name": "Validators.isValidEmail", "file": "Validators.m" }, + "source": { "name": "Validators.validateUser:email:", "file": "Validators.m" }, + "target": { "name": "Validators.isValidEmail:", "file": "Validators.m" }, "kind": "calls", "mode": "same-file", - "notes": "[self isValidEmail:...] — same-class class method call" + "notes": "[self isValidEmail:...] — same-class class method call; full selector includes trailing colon" } ] } diff --git a/tests/benchmarks/resolution/resolution-benchmark.test.ts b/tests/benchmarks/resolution/resolution-benchmark.test.ts index 719f9685b..15845942f 100644 --- a/tests/benchmarks/resolution/resolution-benchmark.test.ts +++ b/tests/benchmarks/resolution/resolution-benchmark.test.ts @@ -167,19 +167,31 @@ const THRESHOLDS: Record = { // TODO(#875): raise scala thresholds once call resolution lands scala: { precision: 0.0, recall: 0.0 }, php: { precision: 0.6, recall: 0.2 }, - // TODO: raise thresholds below once call resolution is implemented for each language - elixir: { precision: 0.0, recall: 0.0 }, + // elixir: cross-module qualified calls resolve at 100% precision, 81% recall. + // Expected-edges now use module-qualified names (Main.run, UserService.create_user, etc.) + // matching what the Elixir extractor emits. Same-module bare calls (display_user → get_user) + // are not yet resolved — tracked as FNs. Precision 1.0 acts as ratchet against future FPs. + elixir: { precision: 1.0, recall: 0.8 }, dart: { precision: 0.0, recall: 0.0 }, zig: { precision: 0.0, recall: 0.0 }, fsharp: { precision: 0.0, recall: 0.0 }, gleam: { precision: 0.0, recall: 0.0 }, clojure: { precision: 0.0, recall: 0.0 }, - julia: { precision: 0.0, recall: 0.0 }, + // julia: cross-module qualified calls resolve at 100% precision, 73% recall. + // Expected-edges now use module-qualified names (App.main, Service.create_user, etc.) + // matching what the Julia extractor emits. Same-file calls (summary → format_summary, etc.) + // are not yet resolved — tracked as FNs. Precision 1.0 acts as ratchet against future FPs. + julia: { precision: 1.0, recall: 0.7 }, r: { precision: 0.0, recall: 0.0 }, erlang: { precision: 0.0, recall: 0.0 }, solidity: { precision: 0.0, recall: 0.0 }, // New fixture languages — no parser or call resolution yet - objc: { precision: 0.0, recall: 0.0 }, + // objc: class-method static calls and same-class calls resolve at 100% precision, 46% recall. + // Expected-edges now use full ObjC selectors (createUserWithId:name:email:, isValidEmail:, etc.) + // matching what the ObjC extractor emits. Constructor message sends (initWithRepository:) and + // receiver-typed instance message sends are not yet resolved — tracked as FNs. + // Precision 1.0 acts as ratchet against future FPs. + objc: { precision: 1.0, recall: 0.4 }, cuda: { precision: 0.0, recall: 0.0 }, groovy: { precision: 0.0, recall: 0.0 }, verilog: { precision: 0.0, recall: 0.0 },