propose: design resolve tool#134
Conversation
Review — propose: design
|
| Surface | Current pre-resolve text |
|---|---|
docs/AGENT-GUIDE.md |
"Identifier resolution (pre-resolve)" section |
mcp_v2.py describe_v2 collision hint_message |
Points at find + search, not resolve |
server.py _INSTRUCTIONS |
Lists four tools only — needs resolve after PR-RESOLVE-1 |
README.md MCP tool list |
Likely needs a fifth tool row when implemented |
Worth adding a short "description sweep checklist" in §6 so PR-RESOLVE-2 doesn't miss non-server.py prose.
4. Test contract vs realistic outcomes
§6 PR-RESOLVE-1 says:
Each
ResolveReasonis exercised by at least onestatus="one"test.
Several reasons (short_name, fqn_suffix, often client_target) are naturally status="many". Tighten to:
- Every
ResolveReasonappears in at least one test (any status), and status="one"/many/none/ loud-fail each have dedicated scenarios.
Otherwise implementers will write awkward tests or weaken generators to force one.
5. Filter-frame §3.4.2 vs this propose (intentional — document the delta)
The completed filter-frame propose allowed a microservice co-parameter on describe for FQN collisions. Shipped code does first match + hint instead (describe_v2). This propose correctly standardizes collisions on resolve(..., hint_kind="symbol") → status="many". Add one sentence in §1 or References: post-resolve, prefer resolve over describe(fqn=…) when FQN may collide; describe may keep first-match behavior or be tightened in a follow-up.
That avoids implementers assuming describe will gain a microservice parameter.
6. ONTOLOGY_VERSION wording in PR-RESOLVE-2
PR-RESOLVE-2 says bump ONTOLOGY_VERSION only if other schema work ships. Good — a new MCP tool should not bump ontology version. Consider stating explicitly: adding resolve does not bump ontology_version.
Per repo rules, put ResolveReason in java_ontology.py (single source of truth) — worth one line in PR-RESOLVE-1 scope.
7. Optional: add plans/PLAN-RESOLVE-TOOL.md when locking
Repo culture expects a plan + CURSOR-PROMPTS-* for multi-PR work. The propose's §6 is already detailed; a thin plan mirroring PR-RESOLVE-1/2 with sentinel greps and exact test_* names would reduce handoff friction. Not blocking for merging this propose.
Smaller nits (non-blocking)
- Namespace: References acknowledge
_resolve_node_kindvs publicresolve— rename internal helper in PR-RESOLVE-1 if confusion is likely. - Candidate dedup: §3.6 doesn't say whether
generate_candidatesdeduplicates bynode.idbefore countinglen(matches). Worth one line so duplicate generator paths don't yield bogusmany. - Truncation at K: If
len(matches) > K, stillstatus="many"with top-K — fine; optional note thatmessagecould mention truncation (telemetry-only, not required v1).
Alignment check with current code
| Propose claim | Master reality |
|---|---|
Pre-resolve fallback in tool descriptions |
Present on search + describe only |
describe(fqn=…) for symbols |
Shipped |
| FQN collision → disambiguation | describe returns first + hint; resolve will own many |
| Wildcards rejected on structured surfaces | Shipped |
find strict target_service (no fuzzy) |
Shipped — "smartcare" → resolve, not find |
No contradictions with ontology 12 or the four-tool surface.
Recommended next steps
- Merge this PR after fixing items 1–2 (and ideally 3–4) in the markdown.
- Open
plans/PLAN-RESOLVE-TOOL.md+plans/CURSOR-PROMPTS-RESOLVE-TOOL.mdwhen starting implementation. - PR-RESOLVE-1: models in
mcp_v2.py, handler + per-kind generators,server.pyregistration, tests per UC table,ResolveReasoninjava_ontology.py. - PR-RESOLVE-2: sweep checklist above + README MCP section.
Summary: The design is ready to lock: strict, composable, and it closes the filter-frame's biggest gap (identifier canonicalization without smart filters). Address the status on loud-fail gap, fix the TL;DR reason typo, widen the PR-RESOLVE-2 doc sweep, and relax the per-reason status="one" test rule — then merge and implement in the stated two-PR order.
Adds propose/RESOLVE-TOOL-PROPOSE.md. `resolve(identifier, hint_kind?)` is the strict-frame primitive for identifier-shaped lookups, named in §3.5 of the filter-frame propose and now designed in detail: - Discriminated output: status=one|many|none, with NodeRef / candidate list / message accordingly. Status invariants checked at handler exit. - hint_kind only — no microservice co-hint, no hints: dict. - Closed ResolveReason Literal (8 values): identifier shape \u2192 reason category, ranking by reason priority + specificity + stable id. - Loud-fail (success=False) on empty / whitespace identifier, distinct from status='none' (well-formed input, no match). - All three kinds in one tool; per-kind splits explicitly out. - Removes the §3.4.7 pre-resolve fallback wording from all four existing tool descriptions (not amended, removed). 17-UC re-walk covers Symbol FQN / suffix / short-name, route method+path / template / microservice-qualified, client target / target+path, malformed input, query-shaped input, wildcards, and cross-kind disambiguation without hint_kind. 15 locked decisions, 2-PR migration (PR-RESOLVE-1 ships the tool, PR-RESOLVE-2 sweeps tool descriptions). No deprecation aliases — breaking changes allowed, no active users.
6138d92 to
0715eac
Compare
|
Thanks for the substantive review. Applied items 1\u20136 and the candidate-dedup nit; pushed back on two non-blockers. Force-pushed the revision (one commit, no noise). Applied(1) (2) (3) PR-RESOLVE-2 sweep is incomplete. \u00a76 now carries a sweep checklist with 8 surface rows: (4) Per-reason (5) Document delta vs filter-frame \u00a73.4.2. Added a paragraph in \u00a71 plus locked decision \u00a77.20: (6) Nit \u2014 candidate dedup. \u00a73.6 now has the dedup step in the decision rule, with a comment that generator paths can overlap (e.g., Drift caught during the revision pass. \u00a75's Not adopted (with reasoning)(7) "Add Nit \u2014 truncation message at K. Holding the line. Locking " Nit \u2014 rename Resulting changes
Total decisions in \u00a77: 20. PR count, kind count, UC count (17), |
Propose: design the
resolvetool — strict-frame identifier resolution.The filter frame (PRs #131 → #133, propose) named
resolvein §3.5 as the escape valve for identifier-shaped lookups but explicitly deferred its design. This propose locks that design.The decision in one line
resolve(identifier: str, hint_kind: NodeKind | None) → ResolveOutputreturns one of three loud states —status="one"with aNodeRef,status="many"with a ranked candidate list and per-candidate reasons, orstatus="none"with a diagnostic message.Shape highlights
status. No silent best-guess; noconfidence: floatshoved into a single-node output.hint_kindonly. Nomicroserviceco-hint, nohints: dict. Cross-microservice FQN collisions surface honestly asstatus="many"with the microservice visible on each candidate'sNodeRef.resolve_symbol/resolve_route/resolve_client) explicitly rejected — same contract, smaller mental model.ResolveReasonLiteral (8 values:exact_id,exact_fqn,fqn_suffix,short_name,route_template,route_method_path,client_target,client_target_path). Adding a reason is a frame decision, like adding anEdgeType.status="many": reason priority → specificity → stablenode.id.scoreis documented as telemetry-only; agents must not branch on it.success=False, distinct fromstatus="none"(well-formed input, no match). Matches the strict-frame loud-fail discipline from lossless-permissive frame: tactical no-regret fix for #117 silent-drop bug class #122.Migration
2 PRs, in order:
resolveend-to-end (models, handler, candidate generators, tests covering eachResolveReasonplus malformed-input cases).resolvefallback wording from all four existing tool descriptions (not amended, removed). Verify by re-reading docstrings: "search + describe-per-candidate" no longer appears as a recommended pattern.No deprecation aliases (no active users, breaking changes allowed).
What lives in §6, §7, §8 of the propose
resolvewhen right tool issearch" failure mode and how the description / UC14 / UC15 wording is supposed to prevent it.Use-case re-walk — 17 rows
Covers Symbol FQN / suffix / short-name, route method+path / template / microservice-qualified, client target / target+path, malformed input, query-shaped input, wildcards (rejected, aligned with filter-frame §3.4.1), and cross-kind disambiguation without
hint_kind. The previously-degraded UC7 of the filter-frame propose ("smartcare" → canonical) is now first-class.How to read
Suggested order: TL;DR → §1 Frame → §3 Proposed surface → §4 Use-case re-walk → §7 Decisions taken. §2 / §5 / §8 are supporting context.
What's out of scope (frame-binding)
microserviceco-hinthints: dictidentifiernodewhen ambiguousresolve_symbol, etc.)describe-payload intoResolveOutputreason: strEdgeType.Doc:
propose/RESOLVE-TOOL-PROPOSE.md