plan: PR-H1 — FEIGN_CLIENT role to CLIENT + HTTP_CLIENT capability#33
Conversation
Switch Feign role inference and flow/ranking role literals to CLIENT, add HTTP_CLIENT capability detection for @FeignClient, and update docs/tool enums to reflect ontology version 9 vocabulary. Add focused rename regression coverage for graph role/capability behavior and brownfield warn-and-drop/acceptance paths. Co-authored-by: Cursor <cursoragent@cursor.com>
Review: PR-H1 —
|
| Sentinel | Status |
|---|---|
MESSAGE_PRODUCER (20 hits) |
✅ docstring/enum-list updates adding HTTP_CLIENT to capability lists + 1 regression test #7 — no async logic touched |
RestTemplate / WebClient (3 each) |
✅ README/CODEBASE_REQUIREMENTS doc-only — no code change, no auto-promotion |
RegisterRestClient (2 hits) |
✅ in CODEBASE_REQUIREMENTS.md doc snippet only (matches the role table format users see); not a Python code change |
@mcp.tool |
✅ 0 matches — no new MCP tools registered |
CREATE NODE TABLE / CREATE REL TABLE / DROP TABLE |
✅ 0 matches — no schema column changes |
_SCHEMA_META / _META_PR_* |
✅ 0 matches — no new tier in the meta() query chain (correctly out of scope per plan) |
alias |
✅ 0 matches — no backwards-compat alias (correct, breaking change explicitly allowed) |
FEIGN_CLIENT literals in production code |
✅ 0 (only an intentional phase-7 comment at ast_java.py:74 documenting the rename) |
Plan compliance (Definition of Done)
| # | Step from plan | Verified |
|---|---|---|
| 1 | ROLE_ANNOTATIONS["FeignClient"] → "CLIENT" |
✅ ast_java.py:92 |
| 2 | _TYPE_ANN_TO_CAPABILITY["FeignClient"] = "HTTP_CLIENT" |
✅ ast_java.py:116 |
| 3 | _FLOW_STAGES[2], _ENTRYPOINT_ROLES, trace_flow docstring |
✅ kuzu_queries.py:997, 1006 |
| 4 | _ROLE_SCORE_WEIGHTS["CLIENT"] = 0.06 |
✅ search_lancedb.py:188 |
| 5 | Six server.py literal references |
✅ all updated, including entry_roles default at server.py:1418 |
| 6 | README.md (3 lines + brownfield note) |
✅ |
| 7 | CODEBASE_REQUIREMENTS.md (146, 162, 346-347) |
✅ |
| 8 | tests/test_lancedb_e2e.py:342 allow-list |
✅ now {"CONTROLLER", "COMPONENT", "SERVICE", "CLIENT"} |
| 9 | ONTOLOGY_VERSION 8 → 9 |
✅ ast_java.py:117 |
| 10 | All 9 new tests in test_client_role_rename.py |
✅ all 9 named per plan, all pass |
Tests
290 passed, 4 skipped in 103.42s
Matches the plan target of 281 + 9 = 290 passed, 4 skipped exactly. No regressions in test_kuzu_queries, test_lancedb_e2e, or any of the previously-green files.
Manual evidence reproduced
Build against tests/fixtures/cross_service_smoke:
CLIENT symbols: ['smoke.a.BFeignClient']
legacy FEIGN_CLIENT count: 0
HTTP_CLIENT symbols: ['smoke.a.BFeignClient']
ontology_version: 9
✅ matches plan expected output exactly.
Brownfield warn-and-drop (test #4 path), with the correct nested YAML shape role_overrides.fqn.<fqn>.role:
[lancedb-mcp] role_overrides.annotations: unknown role 'FEIGN_CLIENT' for 'FeignClient' — dropped
[lancedb-mcp] role_overrides.fqn: unknown role 'FEIGN_CLIENT' for 'smoke.a.BFeignClient' — dropped
BFeignClient role: ['CLIENT']
legacy FEIGN_CLIENT count: 0
✅ override silently dropped (warn-and-drop, not exception), role lands on the auto-detected CLIENT. Plan delta from propose §5.3 is correctly encoded in the test.
Determinism: built twice into /tmp/det_a and /tmp/det_b, sorted (fqn, role, capabilities) tuples for CLIENT symbols are identical.
Notes that earned my trust
- Plan delta on test Add per-PR Cursor task prompts for Tier 1 completion #4 honoured. Asserts stderr
"unknown role"substring + role-stays-CLIENT, matchinggraph_enrich.py:443-447, 481-486reality, not the propose'sraise ValueErrorexample. This is exactly the alignment the plan promised. - Single source-of-truth flips. The two-line change at
ast_java.py:92(role) andast_java.py:116(capability) propagates correctly through the auto-derivedVALID_ROLES/VALID_CAPABILITIES— nojava_ontology.pyedit was needed, and none was made. - Entry-roles list rewritten in two places consistently.
kuzu_queries.py:1006(the_ENTRYPOINT_ROLESconstant) andserver.py:1418(the MCP tool'sentry_rolesdefault) both now have"CLIENT"instead of"FEIGN_CLIENT"— easy to miss one or the other, both got it. - Phase-7 comment at
ast_java.py:74documents the rename inline next to the ontology bump — future-archeology friendly. - No
_SCHEMA_META/meta()tiered chain extension. The plan correctly identified thatSymbol.roleis a STRING column and the new literal needs no schema migration; the diff confirms no tier was added.
Observations (non-blocking)
- Plan's manual-evidence snippet uses an outdated
r['symbol']indexing pattern. The plan at lines 285-288 shows[r['symbol'] for r in g.list_by_role(...)], butKuzuGraph.list_by_roleactually returnsSymbolHitdataclass instances accessed viah.fqn/h.roleetc. Cosmetic; doesn't affect anything that ships, but worth fixing in the plan if anyone later runs the snippet verbatim. Not a blocker — the test suite uses the correct API. RegisterRestClientMicroProfile entry inCODEBASE_REQUIREMENTS.mddoc snippet got upgraded toCLIENTas a side effect of updating the sample role table. This is fine (the doc literal needed updating) but be aware: there's no actualRegisterRestClientannotation handler inast_java.pyyet — that's still followup Add Cursor rules and agent settings for CLI agents #3. Doc snippet is now slightly aspirational. Worth adding a parenthetical "(see followup Add Cursor rules and agent settings for CLI agents #3 for actual support)" in a future doc pass.exposes_suppressed_feign=1still appears in the build summary — this is correct (PR-F1 behaviour), but the metric name keeps the legacyfeigntoken. Out of scope here; could rename toexposes_suppressed_clientin a future cleanup PR if you want full vocabulary alignment.
Plan deltas needed
None. The implementation matches the plan as written, including the warn-and-drop semantics for test #4. (Optional: tweak the plan's manual-evidence snippet to use h.fqn instead of r['symbol'] as noted in observation #1.)
Ready to merge. Next: H1 implementation is complete — when you're ready, the queue items are PR-H1 doc-snippet polish (observations #1, #2 above, optional) and the followups captured in the plan (real-codebase honesty audit, capability-weighted search, @RegisterRestClient actual support, multi-attribution PR #24).
* chore: tidy completed plans/proposes and refresh stale docs Move completed plans to plans/completed/: - PLAN-CLIENT-ROLE-RENAME.md (PR #33 merged) - PLAN-CROSS-SERVICE-RESOLUTION-FLAG.md (PR #30 merged) - PLAN-FEIGN-NOT-AN-EXPOSER.md (PR #31 merged) Move completed proposes to propose/completed/: - CLIENT-ROLE-RENAME-PROPOSE.md (PR #28 merged) - CROSS-SERVICE-RESOLUTION-FLAG-PROPOSE.md (PR #26 merged) - FEIGN-NOT-AN-EXPOSER-PROPOSE.md (PR #25 merged) Refresh active docs: - README.md "Deferred" section: trace_request_flow, find_route_callers, HTTP_CALLS/ASYNC_CALLS are shipped (not deferred). Add explicit pointers to the still-active TIER2-INCREMENTAL-REBUILD and REFRESH-CODE-INDEX-AUTO-MODE proposes for the incremental Kuzu work. - CODEBASE_REQUIREMENTS.md A.3: drop the stale 'ontology version 3' literal (now 9) and fix references to PLAN-CAPABILITIES-MODEL and CALL-GRAPH-PROPOSE to use their completed/ paths. Tense matches reality (call-graph layer is shipped, not deferred). - CODEBASE_REQUIREMENTS.md B.9: same fix for the propose/DEFERRED-CALL-GRAPH-PROPOSE.md reference; the propose lives under propose/completed/CALL-GRAPH-PROPOSE.md. No code changes. Test baseline unchanged: 290 passed, 4 skipped. * docs: add inline Java stubs for @CodebaseRoute / @CodebaseClient / @CodebaseProducer Per pushback on PR #34: the route, client, and producer brownfield annotations were mentioned 4x in README + CODEBASE_REQUIREMENTS but their @interface stubs were never shown inline. Users had to spelunk through tests/fixtures/ to know what to copy into their project. README §5 'Brownfield overrides — Last resort — source stubs' now has three explicit subsections: - 3a. Roles & capabilities — @CodebaseRole / @CodebaseCapability / @CodebaseCapabilities (class-level), with usage example. - 3b. Routes — @CodebaseRoute / @CodebaseRoutes + CodebaseRouteFrameworkKind / CodebaseRouteKind (method-level), with HTTP-endpoint and Kafka-consumer usage examples. - 3c. Clients & producers — @CodebaseClient / @CodebaseClients and @CodebaseProducer / @CodebaseProducers (method-level), with rest_template + kafka_send usage examples. Stub Java in the doc matches the verbatim sources under tests/fixtures/brownfield_route_stubs/ and brownfield_client_stubs/ (also referenced for copy-paste). Enum values mirror VALID_ROUTE_* and VALID_CLIENT_KINDS in java_ontology.py. CODEBASE_REQUIREMENTS.md A.2.1 updated to enumerate all three annotation families (roles, routes, clients/producers) and link to the matching README sections instead of only mentioning role stubs. No code change. Test baseline unchanged: 290 passed, 4 skipped.
Summary
plans/PLAN-CLIENT-ROLE-RENAME.md: rename role literalFEIGN_CLIENTtoCLIENTacross inference, flow stages, entry roles, ranking weights, and MCP tool descriptionsHTTP_CLIENTcapability auto-detection for@FeignClient, bumpONTOLOGY_VERSIONto9, and update README/CODEBASE_REQUIREMENTS for the new vocabulary and reindex requirementtests/test_client_role_rename.py(9 tests) and update the trace-flow e2e role allowlistTest plan
pytest tests/test_client_role_rename.py -qpytest tests/test_kuzu_queries.py -k "trace_flow or list_by_role_controller" -qpytest tests/test_lancedb_e2e.py -q(heavy-gated; skipped in default env)ruff check ast_java.py kuzu_queries.py search_lancedb.py server.py tests/test_client_role_rename.py tests/test_lancedb_e2e.pyMade with Cursor