Skip to content

Commit 587993d

Browse files
HumanBean17claude
andauthored
fix(skills): strip internals from consumer docs, add role filtering guidance (#251)
* fix(skills): strip internal mechanics from consumer docs, add role semantics and filtering guidance Remove brownfield annotations, source_layer, OVERRIDDEN_BY internals, UnresolvedCallSite details, and hint template names from consumer-facing docs (explore-codebase skill and AGENT-GUIDE). Add semantic descriptions for all role enum values and practical filtering guidance so agents can make informed filter choices (e.g. exclude DTO/OTHER/MAPPER for business logic traces). Trim explore-codebase to under 300 lines by removing navigation patterns that duplicate the decision tree. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(skills): remove source_layer, add resolved=false meaning and OVERRIDES bidirectional hint Address PR review feedback: remove source_layer from NodeFilter tables (consumer agents don't need indexer extraction layer names), restore resolved=false meaning for CALLS edges as a short note, and add OVERRIDDEN_BY/OVERRIDES bidirectional equivalence hint for agents who try the wrong direction. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 23d0c32 commit 587993d

2 files changed

Lines changed: 51 additions & 73 deletions

File tree

docs/AGENT-GUIDE.md

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,6 @@ Copy the block between `<!-- BEGIN` and `<!-- END` into your project's `AGENTS.m
3131

3232
When MCP disagrees with the open file, **the file wins**; treat the mismatch as a likely stale or incomplete index.
3333

34-
### Brownfield annotations on methods
35-
36-
If a method has any of these (including plural containers **`@CodebaseHttpRoutes`**, **`@CodebaseAsyncRoutes`**, **`@CodebaseHttpClients`**, **`@CodebaseProducers`**), that annotation is the **only** source for the facets it declares — framework inference on the **same** method is **not merged** for that axis:
37-
38-
| Annotation | Declares | Framework rows bypassed (examples) |
39-
| ---------- | -------- | ------------------------------------ |
40-
| `@CodebaseHttpRoute` | inbound HTTP path / verb | Spring MVC/WebFlux mapping annotations |
41-
| `@CodebaseAsyncRoute` | inbound async topic / route | `@KafkaListener`, `@RabbitListener`, … |
42-
| `@CodebaseHttpClient` | outbound HTTP client call site | `@FeignClient` method mappings, RestTemplate-style inference |
43-
| `@CodebaseProducer` | outbound async producer call site | `KafkaTemplate` / `StreamBridge` producer inference |
44-
45-
Trust the indexed brownfield row over a framework-only reading of the source.
46-
4734
### Workflow (locate → inspect → walk)
4835

4936
1. **Locate**`resolve` for identifier-shaped strings; `search` for natural language or code fragments; `find` for structured `NodeFilter` discovery.
@@ -88,17 +75,15 @@ Use these strings **verbatim** in `neighbors(..., edge_types=[...])`.
8875

8976
| Edge type | Meaning |
9077
| --------- | ------- |
91-
| `OVERRIDDEN_BY` | Concrete overrider methods (stored `[:OVERRIDES]` dispatch hop) |
92-
| `OVERRIDDEN_BY.DECLARES_CLIENT` | Clients declared on overriders (`via_id` = overrider method) |
78+
| `OVERRIDDEN_BY` | Concrete overrider methods |
79+
| `OVERRIDDEN_BY.DECLARES_CLIENT` | Clients declared on overriders |
9380
| `OVERRIDDEN_BY.DECLARES_PRODUCER` | Producers on overriders |
9481
| `OVERRIDDEN_BY.EXPOSES` | Routes exposed by overriders |
9582

96-
**Stored vs virtual direction (base override axis):** `neighbors(decl_id, "out", ["OVERRIDDEN_BY"])` returns the same overrider method ids as `neighbors(decl_id, "in", ["OVERRIDES"])` on the same declaration method. Prefer the dot-key when `describe.edge_summary` advertises `OVERRIDDEN_BY`.
83+
`neighbors(decl_id, "out", ["OVERRIDDEN_BY"])` returns the same overrider methods as `neighbors(decl_id, "in", ["OVERRIDES"])` — prefer the dot-key when `edge_summary` advertises it.
9784

9885
Do not mix `DECLARES.*` and `OVERRIDDEN_BY.*` in one `edge_types` list on a single origin id — the handler rejects the whole request (only one axis applies per node).
9986

100-
`describe` `edge_summary` counts for `OVERRIDDEN_BY*` use the same stored `[:OVERRIDES]` dispatch hop as `neighbors` (ontology **13+** graphs with materialized override edges). Rebuild the index if counts look wrong or dot-key walks return fewer rows than advertised.
101-
10287
**Pagination:** default `neighbors` `limit=25` slices the merged flat + composed edge list. When `edge_summary` shows a large `out` count for a composed key, raise `limit` (and use `offset`) or issue separate calls per key.
10388

10489
### Argument shapes
@@ -150,7 +135,7 @@ For **`find`**, `filter` is required — `{}` means no predicates (all nodes of
150135

151136
| Keys | Applies to |
152137
| ---- | ---------- |
153-
| `microservice`, `module`, `source_layer` | All kinds (`source_layer` mainly **client** / **producer**) |
138+
| `microservice`, `module` | All kinds |
154139
| `role`, `exclude_roles`, `annotation`, `capability`, `fqn_prefix`, `symbol_kind`, `symbol_kinds` | **symbol** |
155140
| `http_method`, `path_prefix`, `framework` | **route** |
156141
| `client_kind`, `target_service`, `target_path_prefix`, `http_method` | **client** |
@@ -174,7 +159,7 @@ For **`find`**, `filter` is required — `{}` means no predicates (all nodes of
174159

175160
Prefer **`resolve``describe(id=…)`** over **`describe(fqn=…)`** when an FQN may collide (`describe(fqn=…)` returns the first row).
176161

177-
**`microservice`** — service where the node lives. **`target_service`** (clients only) — remote service being called. **`source_layer`** (clients/producers) — which extraction layer produced the row (`builtin`, `layer_a_meta`, `layer_b_ann`, `layer_c_source`, `layer_b_fqn`, …). **`role`** (symbols only) — architectural stereotype (`CONTROLLER`, `SERVICE`, …).
162+
**`microservice`** — service where the node lives. **`target_service`** (clients only) — remote service being called. **`role`** (symbols only) — architectural stereotype (`CONTROLLER`, `SERVICE`, …).
178163

179164
### Decision tree
180165

@@ -199,6 +184,7 @@ Prefer **`resolve` → `describe(id=…)`** over **`describe(fqn=…)`** when an
199184

200185
1. **Structure beats vector** for exact questions — use `resolve` / `find` + `neighbors`, not `search`, for "who calls …".
201186
2. **Vector beats structure** for fuzzy discovery — `search` first, then pivot to `describe` / `neighbors`.
187+
3. **Filter by role** to keep traces focused — exclude `DTO`, `OTHER`, `MAPPER` for business logic; target `SERVICE` for orchestration, `REPOSITORY` for data access.
202188

203189
### Tool reference
204190

@@ -216,7 +202,7 @@ Full node + `edge_summary`. Args: `id` (any kind) or `fqn` (symbol only; `id` wi
216202

217203
- **Stored keys** — counts for edges that exist in the graph.
218204
- **Type symbols** (`class`, `interface`, `enum`, `record`, `annotation`) may add composed keys `DECLARES.DECLARES_CLIENT`, `DECLARES.DECLARES_PRODUCER`, `DECLARES.EXPOSES` — navigable via `neighbors` with those dot-keys (`out` only).
219-
- **Method symbols** may add virtual keys `OVERRIDDEN_BY`, `OVERRIDDEN_BY.DECLARES_*`, `OVERRIDDEN_BY.EXPOSES` (navigable via `neighbors` on non-static method origins, `out` only), plus an **`OVERRIDES`** row merging stored `[:OVERRIDES]` incident counts with the rollup dispatch-up count (`max` per direction). Rollup and dot-key traversal both use stored `[:OVERRIDES]` for the dispatch hop. Static methods and constructors do not get override-axis keys.
205+
- **Method symbols** may add virtual keys `OVERRIDDEN_BY`, `OVERRIDDEN_BY.DECLARES_*`, `OVERRIDDEN_BY.EXPOSES` (navigable via `neighbors` on non-static method origins, `out` only), plus an **`OVERRIDES`** row with incident counts. Static methods and constructors do not get override-axis keys.
220206

221207
Composed counts are **edge rows**, not distinct methods; `count > 0` means "there is something to walk".
222208

@@ -228,17 +214,32 @@ Identifier lookup; three statuses above. Args: `identifier`, optional `hint_kind
228214

229215
One hop. Args: `ids` (string or array), **`direction`**, **`edge_types`**, `limit` (default 25), `offset`, optional `filter` on the other node, optional **`edge_filter`** (`edge_types` must be exactly `['CALLS']` — no composed dot-keys or second stored label; fail-loud otherwise).
230216

231-
**Multiple origin ids:** each id loads the full CALLS stream (or generic hop) in list order; `offset`/`limit` apply to the **concatenated** edge list (`ids[0]` edges first, then `ids[1]`, …), not global source order across origins — a large first origin can leave no rows for later ids within the same page. High fan-out methods are slow; prefer one id per call or a smaller `limit`. **Hints:** `TPL_NEIGHBORS_CALLS_HIGH_FANOUT` / `TPL_NEIGHBORS_CALLS_HAS_UNRESOLVED` fire only for a **single** origin id (multi-origin CALLS skips those nudges).
217+
**Multiple origin ids:** `offset`/`limit` apply to the **concatenated** edge list (`ids[0]` edges first, then `ids[1]`, …). A large first origin can leave no rows for later ids within the same page. Prefer one id per call or raise `limit`.
232218

233219
Returns **edges** with `attrs` (`confidence`, `strategy`, `match`, … on cross-service edges) and **`other`** node.
234220

235221
**Cross-service edges** (`HTTP_CALLS`, `ASYNC_CALLS`): read `attrs.confidence` and `attrs.match` — low confidence or `unresolved`/`phantom`/`ambiguous` means treat as a resolver signal, not ground truth.
236222

237-
**`CALLS` edges:** source-ordered (`call_site_line`, `call_site_byte`). After ontology 15 PR-3, true receiver-failure sites are **not** on `CALLS` — they are `UnresolvedCallSite` nodes (`reason`: `chained_receiver` or `phantom_unresolved_receiver`; ids use the `ucs:` prefix, `other.kind=unresolved_call_site` — **not** describable via `describe(id=…)`). `UNRESOLVED_AT` is graph storage only (not in `EDGE_SCHEMA` / `neighbors` edge_types). `attrs.resolved=false` on remaining `CALLS` rows means known-receiver-external (JDK/Spring) callees, not receiver failure. **`include_unresolved=True`** (CALLS + `direction=out` only) interleaves unresolved sites with resolved `CALLS` (`row_kind` discriminator); **mutually exclusive with `edge_filter`**. **`dedup_calls=True`** collapses identical `(origin, callee)` `CALLS` to one row with `call_site_lines`. **`filter` + `edge_filter` together** load the ordered CALLS stream then apply callee `NodeFilter` in Python — expect higher latency on hot methods than `edge_filter` alone. Optional **`edge_filter`** projects before pagination: `min_confidence`; `include_strategies` / `exclude_strategies` (mutually exclusive); `callee_declaring_role`, `callee_declaring_roles`, `exclude_callee_declaring_roles` (`["OTHER"]` also drops known-external rows). **`filter.role` filters the neighbor method (usually `OTHER`), not the callee stereotype** — use `edge_filter.callee_declaring_role` for repository/service hops. **`exclude_external` applies to `find_callers` / `find_callees` only** (FQN-prefix); trim JDK noise on `neighbors` CALLS via `edge_filter`. Accessor noise: role excludes help; getter/setter heuristics in [`propose/completed/AGENT-SKILLS-AND-COMMANDS-PROPOSE.md`](../propose/completed/AGENT-SKILLS-AND-COMMANDS-PROPOSE.md) `/mini-map`.
223+
**`CALLS` edges:** source-ordered (`call_site_line`, `call_site_byte`). `attrs.resolved=false` means the callee is external (JDK/Spring) — not a missing symbol. **`include_unresolved=True`** (CALLS + `direction=out` only) interleaves unresolved call sites with resolved `CALLS` (`row_kind` discriminator); **mutually exclusive with `edge_filter`**. **`dedup_calls=True`** collapses identical `(origin, callee)` pairs to one row with `call_site_lines`. Optional **`edge_filter`** projects before pagination: `min_confidence`; `include_strategies` / `exclude_strategies` (mutually exclusive); `callee_declaring_role`, `callee_declaring_roles`, `exclude_callee_declaring_roles` (`["OTHER"]` also drops known-external rows). **Note:** `filter.role` filters the neighbor node, not the callee's declaring type — use `edge_filter.callee_declaring_role` for callee stereotype filtering.
238224

239225
### Ontology glossary
240226

241-
**Roles (`filter.role` / `exclude_roles`):** `CONTROLLER`, `SERVICE`, `REPOSITORY`, `COMPONENT`, `CONFIG`, `ENTITY`, `CLIENT`, `MAPPER`, `DTO`, `OTHER`.
227+
**Roles** (`filter.role` / `exclude_roles`):
228+
229+
| Role | Meaning |
230+
| ---- | ------- |
231+
| `CONTROLLER` | HTTP / messaging entry point |
232+
| `SERVICE` | Business logic orchestration |
233+
| `REPOSITORY` | Data access (JPA, JDBC) |
234+
| `COMPONENT` | General Spring component |
235+
| `CONFIG` | `@Configuration` class |
236+
| `ENTITY` | JPA / persistence entity |
237+
| `CLIENT` | Outbound HTTP call wrapper (Feign, RestTemplate, WebClient) |
238+
| `MAPPER` | Data mapper / converter |
239+
| `DTO` | Data transfer object — data carrier, no logic |
240+
| `OTHER` | Infrastructure / utility / framework / JDK / unclassified |
241+
242+
**Filtering with roles:** `DTO`, `OTHER`, and `MAPPER` are data carriers and infrastructure — exclude them with `exclude_roles` or `edge_filter.exclude_callee_declaring_roles` when tracing business logic. On `CALLS` `out` edges, use `edge_filter={"exclude_callee_declaring_roles": ["OTHER"]}` to drop JDK/Spring/framework calls. Use `filter.role` to target a specific layer (e.g. `role=SERVICE` for business logic, `role=REPOSITORY` for data access).
242243

243244
**Capabilities (`filter.capability`):** `MESSAGE_LISTENER`, `MESSAGE_PRODUCER`, `HTTP_CLIENT`, `SCHEDULED_TASK`, `EXCEPTION_HANDLER`.
244245

0 commit comments

Comments
 (0)