java-codebase-rag 0.6.2__py3-none-any.whl → 0.6.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
java_codebase_rag/cli.py CHANGED
@@ -131,15 +131,41 @@ def _run_with_pipeline_progress(
131
131
  cfg: ResolvedOperatorConfig,
132
132
  *,
133
133
  quiet: bool,
134
- work: Callable[[], int],
134
+ verbose: bool = False,
135
+ work: Callable[["PipelineProgress | None"], int],
135
136
  ) -> int:
136
- if quiet:
137
- return int(work())
137
+ """Run ``work`` under the unified progress renderer (default TTY mode only).
138
+
139
+ ``work`` receives a :class:`PipelineProgress` whose ``on_progress`` callback
140
+ should be forwarded to the graph/vectors pipeline helpers so their
141
+ ``JCIRAG_PROGRESS`` events feed the renderer. In ``--quiet`` or ``--verbose``
142
+ mode the context is ``None`` (no Live region: quiet is silent, verbose
143
+ raw-relays subprocess output).
144
+ """
145
+ if quiet or verbose:
146
+ return int(work(None))
147
+ from java_codebase_rag.progress import build_index_progress_context
148
+
149
+ # PR-3 owns all three tasks in order: Vectors → Optimize → Graph. The vectors
150
+ # task is fed by the cocoindex child's per-file ticks + approximate total
151
+ # (subprocess transport, parsed by ProgressRelay); the optimize task is fed
152
+ # in-process by lance_optimize; the graph task is fed by the build_ast_graph
153
+ # child (subprocess transport). A task only becomes visible/running once its
154
+ # first event arrives.
155
+ renderer, on_progress, console = build_index_progress_context()
156
+ progress = PipelineProgress(renderer=renderer)
157
+ progress.on_progress = on_progress
158
+ progress.console = console
159
+
138
160
  _pipeline_header(subcommand, cfg)
139
161
  t0 = time.perf_counter()
140
162
  code = 0
163
+ # start() always flips _started (the non-TTY fallback is a no-op for Live but
164
+ # still needs the flag so apply() routes to the concise-line printer). The
165
+ # TTY Live region is entered inside start() only when the console is a TTY.
166
+ renderer.start()
141
167
  try:
142
- code = int(work())
168
+ code = int(work(progress))
143
169
  return code
144
170
  except BaseException as exc:
145
171
  # Keep footer aligned with process outcome (main maps unhandled Exception -> exit 2).
@@ -155,9 +181,26 @@ def _run_with_pipeline_progress(
155
181
  code = 2
156
182
  raise
157
183
  finally:
184
+ renderer.stop()
158
185
  _pipeline_footer(subcommand, t0, code)
159
186
 
160
187
 
188
+ class PipelineProgress:
189
+ """Progress context handed to ``work``: the renderer + a ready ``on_progress``.
190
+
191
+ ``on_progress``/``console`` are wired by :func:`_run_with_pipeline_progress`
192
+ and should be forwarded to the pipeline helpers' ``on_progress`` /
193
+ ``on_progress_console`` parameters. ``console`` is the renderer's stderr
194
+ ``rich.Console`` so the subprocess drain routes non-progress lines through
195
+ ``console.print`` while the Live region is up (single-writer invariant).
196
+ """
197
+
198
+ def __init__(self, *, renderer: "object | None") -> None:
199
+ self.renderer = renderer
200
+ self.on_progress: "Callable | None" = None
201
+ self.console: "object | None" = None
202
+
203
+
161
204
  def _jsonable(value: Any) -> Any:
162
205
  if hasattr(value, "model_dump"):
163
206
  return value.model_dump()
@@ -208,7 +251,7 @@ def _startup_hints(cfg: ResolvedOperatorConfig) -> None:
208
251
 
209
252
  def _add_index_embedding_flags(p: argparse.ArgumentParser) -> None:
210
253
  p.add_argument("--source-root", type=str, default=None, help="Java repository root (default: cwd)")
211
- p.add_argument("--index-dir", type=str, default=None, help="Index directory (Lance + Kuzu + cocoindex state)")
254
+ p.add_argument("--index-dir", type=str, default=None, help="Index directory (Lance + LadybugDB + cocoindex state)")
212
255
  p.add_argument("--embedding-model", type=str, default=None, help="Override SBERT_MODEL / YAML embedding.model")
213
256
  p.add_argument("--embedding-device", type=str, default=None, help="Override SBERT_DEVICE / YAML embedding.device")
214
257
 
@@ -266,7 +309,7 @@ def _cmd_init(args: argparse.Namespace) -> int:
266
309
  return 2
267
310
  cfg.index_dir.mkdir(parents=True, exist_ok=True)
268
311
 
269
- def work() -> int:
312
+ def work(progress: "PipelineProgress | None") -> int:
270
313
  env = cfg.subprocess_env()
271
314
  verbose = bool(args.verbose)
272
315
  coco = run_cocoindex_update(
@@ -275,6 +318,8 @@ def _cmd_init(args: argparse.Namespace) -> int:
275
318
  quiet=bool(args.quiet),
276
319
  verbose=verbose,
277
320
  lance_project_root=None if args.quiet else cfg.source_root,
321
+ on_progress=progress.on_progress if progress is not None else None,
322
+ on_progress_console=progress.console if progress is not None else None,
278
323
  )
279
324
  if coco.returncode != 0:
280
325
  _emit(
@@ -295,6 +340,8 @@ def _cmd_init(args: argparse.Namespace) -> int:
295
340
  verbose=verbose,
296
341
  quiet=bool(args.quiet),
297
342
  env=env,
343
+ on_progress=progress.on_progress if progress is not None else None,
344
+ on_progress_console=progress.console if progress is not None else None,
298
345
  )
299
346
  if g.returncode != 0:
300
347
  _emit(
@@ -310,7 +357,9 @@ def _cmd_init(args: argparse.Namespace) -> int:
310
357
  _emit({"success": True, "message": "init completed"})
311
358
  return 0
312
359
 
313
- return _run_with_pipeline_progress("init", cfg, quiet=bool(args.quiet), work=work)
360
+ return _run_with_pipeline_progress(
361
+ "init", cfg, quiet=bool(args.quiet), verbose=bool(args.verbose), work=work
362
+ )
314
363
 
315
364
 
316
365
  def _cmd_increment(args: argparse.Namespace) -> int:
@@ -323,7 +372,7 @@ def _cmd_increment(args: argparse.Namespace) -> int:
323
372
  if vectors_only:
324
373
  _emit_increment_ladybug_warning()
325
374
 
326
- def work() -> int:
375
+ def work(progress: "PipelineProgress | None") -> int:
327
376
  env = cfg.subprocess_env()
328
377
  coco = run_cocoindex_update(
329
378
  env,
@@ -331,6 +380,8 @@ def _cmd_increment(args: argparse.Namespace) -> int:
331
380
  quiet=bool(args.quiet),
332
381
  verbose=bool(args.verbose),
333
382
  lance_project_root=None if args.quiet else cfg.source_root,
383
+ on_progress=progress.on_progress if progress is not None else None,
384
+ on_progress_console=progress.console if progress is not None else None,
334
385
  )
335
386
  if coco.returncode != 0:
336
387
  _emit(
@@ -356,6 +407,8 @@ def _cmd_increment(args: argparse.Namespace) -> int:
356
407
  verbose=bool(args.verbose),
357
408
  quiet=bool(args.quiet),
358
409
  env=env,
410
+ on_progress=progress.on_progress if progress is not None else None,
411
+ on_progress_console=progress.console if progress is not None else None,
359
412
  )
360
413
 
361
414
  # Check if incremental fell back to full rebuild
@@ -389,7 +442,9 @@ def _cmd_increment(args: argparse.Namespace) -> int:
389
442
  _emit({"success": True, "message": "increment completed (Lance + graph updated)"})
390
443
  return 0
391
444
 
392
- return _run_with_pipeline_progress("increment", cfg, quiet=bool(args.quiet), work=work)
445
+ return _run_with_pipeline_progress(
446
+ "increment", cfg, quiet=bool(args.quiet), verbose=bool(args.verbose), work=work
447
+ )
393
448
 
394
449
 
395
450
  def _cmd_reprocess(args: argparse.Namespace) -> int:
@@ -397,14 +452,18 @@ def _cmd_reprocess(args: argparse.Namespace) -> int:
397
452
  _startup_hints(cfg)
398
453
  cfg.apply_to_os_environ()
399
454
 
400
- def work() -> int:
455
+ def work(progress: "PipelineProgress | None") -> int:
401
456
  env = cfg.subprocess_env()
402
457
  verbose = bool(args.verbose)
403
458
  vectors_only = bool(getattr(args, "vectors_only", False))
404
459
  graph_only = bool(getattr(args, "graph_only", False))
405
460
 
406
461
  if vectors_only:
407
- coco = run_cocoindex_update(env, full_reprocess=True, quiet=bool(args.quiet), verbose=verbose)
462
+ coco = run_cocoindex_update(
463
+ env, full_reprocess=True, quiet=bool(args.quiet), verbose=verbose,
464
+ on_progress=progress.on_progress if progress is not None else None,
465
+ on_progress_console=progress.console if progress is not None else None,
466
+ )
408
467
  if _is_cocoindex_preflight_blocker(coco):
409
468
  payload: dict[str, Any] = {
410
469
  "success": False,
@@ -443,6 +502,8 @@ def _cmd_reprocess(args: argparse.Namespace) -> int:
443
502
  verbose=verbose,
444
503
  quiet=bool(args.quiet),
445
504
  env=env,
505
+ on_progress=progress.on_progress if progress is not None else None,
506
+ on_progress_console=progress.console if progress is not None else None,
446
507
  )
447
508
  if _is_graph_preflight_blocker(g):
448
509
  payload = {
@@ -475,14 +536,23 @@ def _cmd_reprocess(args: argparse.Namespace) -> int:
475
536
  _emit_reprocess_outcome(payload, selective_tty_mode="graph" if ok else None)
476
537
  return _reprocess_exit_code(payload)
477
538
 
478
- import server # lazy: pulls sentence_transformers/torch/lancedb/kuzu
539
+ import server # lazy: pulls sentence_transformers/torch/lancedb/ladybug
479
540
 
480
- result = asyncio.run(server.run_refresh_pipeline(quiet=bool(args.quiet), verbose=verbose))
541
+ result = asyncio.run(
542
+ server.run_refresh_pipeline(
543
+ quiet=bool(args.quiet),
544
+ verbose=verbose,
545
+ on_progress=progress.on_progress if progress is not None else None,
546
+ on_progress_console=progress.console if progress is not None else None,
547
+ )
548
+ )
481
549
  payload = result.model_dump()
482
550
  _emit_reprocess_outcome(payload)
483
551
  return _reprocess_exit_code(payload)
484
552
 
485
- return _run_with_pipeline_progress("reprocess", cfg, quiet=bool(args.quiet), work=work)
553
+ return _run_with_pipeline_progress(
554
+ "reprocess", cfg, quiet=bool(args.quiet), verbose=bool(args.verbose), work=work
555
+ )
486
556
 
487
557
 
488
558
  def _cmd_install(args: argparse.Namespace) -> int:
@@ -495,6 +565,7 @@ def _cmd_install(args: argparse.Namespace) -> int:
495
565
  model=args.model,
496
566
  source_root=None, # None means cwd; installer confirms interactively
497
567
  quiet=bool(args.quiet),
568
+ verbose=bool(args.verbose),
498
569
  )
499
570
 
500
571
 
@@ -504,6 +575,8 @@ def _cmd_update(args: argparse.Namespace) -> int:
504
575
  return run_update(
505
576
  force=bool(args.force),
506
577
  dry_run=bool(args.dry_run),
578
+ quiet=bool(args.quiet),
579
+ verbose=bool(args.verbose),
507
580
  )
508
581
 
509
582
 
@@ -537,7 +610,7 @@ def _cmd_erase(args: argparse.Namespace) -> int:
537
610
  print("Aborted.", file=sys.stderr)
538
611
  return 2
539
612
 
540
- def work() -> int:
613
+ def work(progress: "PipelineProgress | None") -> int:
541
614
  env = cfg.subprocess_env()
542
615
  drop = run_cocoindex_drop(env, quiet=bool(args.quiet))
543
616
  if drop.returncode == 127:
@@ -570,7 +643,7 @@ def _cmd_erase(args: argparse.Namespace) -> int:
570
643
  _emit({"success": True, "message": "erase completed"})
571
644
  return 0
572
645
 
573
- return _run_with_pipeline_progress("erase", cfg, quiet=bool(args.quiet), work=work)
646
+ return _run_with_pipeline_progress("erase", cfg, quiet=bool(args.quiet), verbose=bool(getattr(args, "verbose", False)), work=work)
574
647
 
575
648
 
576
649
  def _cmd_meta(args: argparse.Namespace) -> int:
@@ -642,7 +715,7 @@ def _cmd_unresolved_calls_list(args: argparse.Namespace) -> int:
642
715
  from ladybug_queries import LadybugGraph # lazy
643
716
 
644
717
  if not LadybugGraph.exists():
645
- _emit({"success": False, "message": "Kuzu graph not found"})
718
+ _emit({"success": False, "message": "LadybugDB graph not found"})
646
719
  return 1
647
720
  graph = LadybugGraph.get()
648
721
  rows = graph.list_unresolved_call_sites(
@@ -663,7 +736,7 @@ def _cmd_unresolved_calls_stats(args: argparse.Namespace) -> int:
663
736
  from ladybug_queries import LadybugGraph # lazy
664
737
 
665
738
  if not LadybugGraph.exists():
666
- _emit({"success": False, "message": "Kuzu graph not found"})
739
+ _emit({"success": False, "message": "LadybugDB graph not found"})
667
740
  return 1
668
741
  graph = LadybugGraph.get()
669
742
  buckets = graph.stats_unresolved_call_sites(by=args.by)
@@ -688,7 +761,7 @@ def _cmd_analyze_pr(args: argparse.Namespace) -> int:
688
761
  from ladybug_queries import LadybugGraph # lazy
689
762
 
690
763
  if not LadybugGraph.exists():
691
- _emit({"success": False, "message": "Kuzu graph not found"})
764
+ _emit({"success": False, "message": "LadybugDB graph not found"})
692
765
  return 1
693
766
  graph = LadybugGraph.get()
694
767
  report = pr_analysis.analyze_pr_pipeline(graph, diff_text)
@@ -728,7 +801,7 @@ def build_parser() -> argparse.ArgumentParser:
728
801
  help="Create a fresh index from a Java repository.",
729
802
  description=(
730
803
  "First-time index creation. Refuses if the resolved index directory "
731
- "already contains a Kuzu graph or Lance tables. Exit 2 on refusal."
804
+ "already contains a LadybugDB graph or Lance tables. Exit 2 on refusal."
732
805
  ),
733
806
  )
734
807
  _add_index_embedding_flags(init)
@@ -797,7 +870,7 @@ def build_parser() -> argparse.ArgumentParser:
797
870
  increment = subparsers.add_parser(
798
871
  "increment",
799
872
  help="Pick up changes since the last index update.",
800
- description="Runs cocoindex catch-up and incremental Kuzu graph update. Use --vectors-only to skip graph update.",
873
+ description="Runs cocoindex catch-up and incremental LadybugDB graph update. Use --vectors-only to skip graph update.",
801
874
  )
802
875
  _add_index_embedding_flags(increment)
803
876
  _add_verbosity_flags(increment)
@@ -810,9 +883,9 @@ def build_parser() -> argparse.ArgumentParser:
810
883
 
811
884
  reprocess = subparsers.add_parser(
812
885
  "reprocess",
813
- help="Rebuild vectors and/or Kuzu (default: both full phases).",
886
+ help="Rebuild vectors and/or LadybugDB (default: both full phases).",
814
887
  description=(
815
- "Default: full Lance reprocess (cocoindex --full-reprocess) then full Kuzu graph rebuild. "
888
+ "Default: full Lance reprocess (cocoindex --full-reprocess) then full LadybugDB graph rebuild. "
816
889
  "Use --vectors-only or --graph-only to run a single phase (mutually exclusive)."
817
890
  ),
818
891
  )
@@ -834,7 +907,7 @@ def build_parser() -> argparse.ArgumentParser:
834
907
  erase = subparsers.add_parser(
835
908
  "erase",
836
909
  help="Delete the index from disk.",
837
- description="Runs cocoindex drop, removes Kuzu, and drops Lance tables. Requires --yes or TTY confirmation.",
910
+ description="Runs cocoindex drop, removes LadybugDB, and drops Lance tables. Requires --yes or TTY confirmation.",
838
911
  )
839
912
  _add_index_embedding_flags(erase)
840
913
  erase.add_argument("--yes", action="store_true", help="Confirm destructive deletion (required in CI)")
@@ -1,10 +1,7 @@
1
1
  """TTY-aware ANSI formatting for CLI stderr progress."""
2
2
  from __future__ import annotations
3
3
 
4
- import itertools
5
4
  import sys
6
- import threading
7
- import time
8
5
 
9
6
  _RESET = "\033[0m"
10
7
  _BOLD = "\033[1m"
@@ -16,8 +13,6 @@ _CYAN = "\033[36m"
16
13
  CHECK = "✓"
17
14
  CROSS = "✗"
18
15
 
19
- _SPINNER_FRAMES = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
20
-
21
16
  _NOISE_CONTAINS: tuple[bytes, ...] = (
22
17
  b"lance::",
23
18
  b"FutureWarning",
@@ -25,6 +20,14 @@ _NOISE_CONTAINS: tuple[bytes, ...] = (
25
20
  b'"event": "brownfield-',
26
21
  b"unknown producer source strategy",
27
22
  b"unknown client source strategy",
23
+ # Builder verbose heartbeats / pass banners: in default mode the renderer's
24
+ # bar subsumes these, so they must NOT also appear as raw lines above the
25
+ # Live region. --verbose raw-relay bypasses this filter and still shows them.
26
+ b"[graph] pass ",
27
+ b"[graph] scoped write ",
28
+ b"[graph] writing ",
29
+ b"[graph] done ",
30
+ b"[increment] ",
28
31
  )
29
32
 
30
33
 
@@ -80,33 +83,3 @@ def styled_check() -> str:
80
83
 
81
84
  def styled_cross() -> str:
82
85
  return red(CROSS) if stderr_is_tty() else CROSS
83
-
84
-
85
- class Spinner:
86
- """Braille spinner that overwrites the current stderr line until stopped."""
87
-
88
- def __init__(self, label: str) -> None:
89
- self._label = label
90
- self._stop = threading.Event()
91
- self._thread: threading.Thread | None = None
92
-
93
- def start(self) -> None:
94
- self._thread = threading.Thread(target=self._run, name="spinner", daemon=True)
95
- self._thread.start()
96
-
97
- def stop(self) -> None:
98
- self._stop.set()
99
- if self._thread is not None:
100
- self._thread.join(timeout=2.0)
101
- sys.stderr.buffer.write(b"\r\x1b[2K")
102
- sys.stderr.buffer.flush()
103
-
104
- def _run(self) -> None:
105
- frames = itertools.cycle(_SPINNER_FRAMES)
106
- t0 = time.monotonic()
107
- while not self._stop.wait(0.3):
108
- elapsed = time.monotonic() - t0
109
- frame = next(frames)
110
- line = f"\r{frame} {self._label} · {elapsed:.0f}s"
111
- sys.stderr.buffer.write(line.encode())
112
- sys.stderr.buffer.flush()
@@ -3,26 +3,10 @@ from __future__ import annotations
3
3
 
4
4
  import asyncio
5
5
  import sys
6
+ from typing import Callable
6
7
 
7
- from java_codebase_rag.cli_format import bold_cyan, is_noise_line, styled_check, styled_cross
8
-
9
-
10
- def emit_vectors_start() -> None:
11
- print(
12
- bold_cyan("[vectors]") + " running · cocoindex update",
13
- file=sys.stderr,
14
- flush=True,
15
- )
16
-
17
-
18
- def emit_vectors_finish(*, elapsed_s: float, exit_code: int) -> None:
19
- marker = styled_check() if exit_code == 0 else styled_cross()
20
- print(
21
- f"{marker} {bold_cyan('[vectors]')} finished · {elapsed_s:.2f}s"
22
- + (f" (exit={exit_code})" if exit_code != 0 else ""),
23
- file=sys.stderr,
24
- flush=True,
25
- )
8
+ from java_codebase_rag.cli_format import is_noise_line
9
+ from java_codebase_rag.progress import ProgressEvent, make_relay
26
10
 
27
11
 
28
12
  class _AsyncLineFilter:
@@ -61,8 +45,15 @@ async def accumulate_and_relay_subprocess_streams(
61
45
  *,
62
46
  relay: bool,
63
47
  verbose: bool = True,
48
+ on_progress: Callable[[ProgressEvent], None] | None = None,
49
+ on_progress_console: object | None = None,
64
50
  ) -> tuple[bytes, bytes]:
65
- """Read stdout and stderr until EOF; optionally copy non-noise stderr chunks to stderr."""
51
+ """Read stdout and stderr until EOF; optionally copy non-noise stderr chunks to stderr.
52
+
53
+ When ``on_progress`` is set, stderr is drained through a :class:`ProgressRelay`
54
+ so ``JCIRAG_PROGRESS`` lines are parsed and routed to ``on_progress`` (and
55
+ suppressed from the relay), matching the sync ``pipeline._popen_capturing_stderr``.
56
+ """
66
57
  stdout = proc.stdout
67
58
  stderr = proc.stderr
68
59
  if stdout is None or stderr is None:
@@ -70,7 +61,12 @@ async def accumulate_and_relay_subprocess_streams(
70
61
 
71
62
  out_buf = bytearray()
72
63
  err_buf = bytearray()
73
- filt = _AsyncLineFilter() if (relay and not verbose) else None
64
+ if on_progress is not None:
65
+ filt = make_relay(on_progress, console=on_progress_console, verbose=verbose)
66
+ elif relay and not verbose:
67
+ filt = _AsyncLineFilter()
68
+ else:
69
+ filt = None
74
70
 
75
71
  async def drain_stdout(reader: asyncio.StreamReader, target: bytearray) -> None:
76
72
  while True:
@@ -151,15 +151,15 @@ Simple types in parentheses; generics erased. No spaces after commas. No-arg: `(
151
151
 
152
152
  ### Shared NodeFilter
153
153
 
154
- For `find`, `filter` is required — `{}` means no predicates. **Strict frame:** unknown keys or inapplicable populated fields → `success=false`.
154
+ For `find`, `filter` is required — `{}` means no predicates. **Strict frame:** unknown keys or inapplicable populated fields → `success=false`; invalid enum values (e.g. wrong case) are rejected earlier at the schema layer with the valid set listed.
155
155
 
156
156
  | Keys | Applies to |
157
157
  | ---- | ---------- |
158
158
  | `microservice`, `module` | All kinds |
159
159
  | `role`, `exclude_roles`, `annotation`, `capability`, `fqn_prefix`, `symbol_kind`, `symbol_kinds` | **symbol** |
160
160
  | `http_method`, `path_prefix`, `framework` | **route** |
161
- | `client_kind`, `target_service`, `target_path_prefix`, `http_method` | **client** |
162
- | `producer_kind`, `topic_prefix` | **producer** |
161
+ | `source_layer`, `client_kind`, `target_service`, `target_path_prefix`, `http_method` | **client** |
162
+ | `source_layer`, `producer_kind`, `topic_prefix` | **producer** |
163
163
 
164
164
  No wildcards in prefix fields — use `search(query=…)` for fuzzy text.
165
165
 
@@ -125,15 +125,15 @@ Use these strings **verbatim** in `neighbors(..., edge_types=[...])`.
125
125
 
126
126
  ### NodeFilter (`find`, `search.filter`, `neighbors.filter`)
127
127
 
128
- For `find`, `filter` is required — `{}` means no predicates. **Strict frame:** unknown keys or inapplicable populated fields → `success=false`.
128
+ For `find`, `filter` is required — `{}` means no predicates. **Strict frame:** unknown keys or inapplicable populated fields → `success=false`; invalid enum values (e.g. wrong case) are rejected earlier at the schema layer with the valid set listed.
129
129
 
130
130
  | Applicable to | Keys |
131
131
  | ------------- | ---- |
132
132
  | All kinds | `microservice`, `module` |
133
133
  | **symbol** only | `role`, `exclude_roles`, `annotation`, `capability`, `fqn_prefix`, `symbol_kind`, `symbol_kinds` |
134
134
  | **route** only | `http_method`, `path_prefix`, `framework` |
135
- | **client** only | `client_kind`, `target_service`, `target_path_prefix`, `http_method` |
136
- | **producer** only | `producer_kind`, `topic_prefix` |
135
+ | **client** only | `source_layer`, `client_kind`, `target_service`, `target_path_prefix`, `http_method` |
136
+ | **producer** only | `source_layer`, `producer_kind`, `topic_prefix` |
137
137
 
138
138
  No wildcards in prefix fields — use `search(query=…)` for ranked text.
139
139
 
@@ -166,8 +166,8 @@ Exclude `DTO`, `OTHER`, `MAPPER` with `exclude_roles` when tracing business logi
166
166
 
167
167
  **Symbol kinds:** `class`, `interface`, `enum`, `record`, `annotation`, `method`, `constructor`.
168
168
 
169
- **Route frameworks:** `spring_mvc`, `webflux`, `kafka`, `rabbitmq`, `jms`, `stream`, `codebase_async_route`,
170
- **Client kinds:** `feign_method`, `rest_template`, `web_client`. **Producer kinds:** `kafka_send`, `stream_bridge_send`.
169
+ **Route frameworks:** `spring_mvc`, `webflux`. (Route *kinds* are `http_endpoint`, `http_consumer`, `kafka_topic`, `rabbit_queue`, `jms_destination`, `stream_binding`.)
170
+ **Client kinds:** `feign_method`, `rest_template`, `web_client`. **Producer kinds:** `kafka_send`, `stream_bridge_send`. **Source layers (client/producer):** `builtin`, `layer_a_meta`, `layer_b_ann`, `layer_b_fqn`, `layer_c_source`.
171
171
  **Match types:** `cross_service`, `intra_service`, `ambiguous`, `phantom`, `unresolved`.
172
172
 
173
173
  ---