Repository navigation
fix: catch the CLI up with the current API - #132
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Polylane could not verify the production impact of this pull request. Checked nominal-api-cloud-infra-prod (40,367 requests, 0 failed invocations, 96h) and CLI telemetry (595 command events / 96h). The extra node-list requests per command are immaterial, and the endpoints the removed commands called were already gone. Also considered · 4 refuted
Analysed against 9 cloud accounts and 1 repository
Polylane could not find the cloud resources this repository manages, so this review looked at the entire cloud account. Connect this repository to its resources and the next review will focus on exactly what this code deploys to. Polylane analysed Rate this review: 👍 helpful · 👎 not helpful · 😕 confusing |
There was a problem hiding this comment.
Changes requested: 2 issues
-
🟡 Minor ·
src/commands/service/id.ts:88
Queries containing underscores miss matching service names and aliases. -
🟡 Minor ·
src/commands/service/id.ts:102
Truncating candidates before exact matching can reject the right service or silently choose an ambiguous one.
Reviewed at 66b6cfe.
For agents
Rationale: Service lookup loses underscore matches and makes exact-match decisions after discarding candidates.
| Severity | Finding | Where |
|---|---|---|
| minor | F1 Queries containing underscores miss matching service names and aliases. | src/commands/service/id.ts:88 |
| minor | F2 Truncating candidates before exact matching can reject the right service or silently choose an ambiguous one. | src/commands/service/id.ts:102 |
F1 invariant: An underscore in the query must not disable substring matching across service IDs, names and alternate names, whether finding services or resolving short names.
- [C1] Unscoped service find payments_api with a node whose id is prod-payments_api-worker and whose other name fields do not match. → Return the node because its ID contains the query.
- [C2] Provider-scoped service find payments_api with a node whose data.name is prod-payments_api-worker. → Return the matching node within the requested provider.
- [C3] Type-scoped service find payments_api with a node whose alternateNames contains payments_api and whose ID and data.name are unrelated. → Return the node within the requested type, despite alternate names being stored as a JSON array.
- [C4] Service find payments_api with both provider and type supplied and a matching alternate name. → Return the matching node while preserving both scope filters.
- [C5] Short-name resolution of payments_api for service show, logs, metrics or graph when the only matching resource has that alternate name. → Resolve that resource rather than reporting that no service exists.
- [C6] A fully qualified composite service ID containing underscores is supplied to a resolver caller. → Preserve the existing strict-ID bypass and do not perform substring queries.
F2 invariant: Short-name resolution must establish exact-match preference and ambiguity before candidate truncation; neither per-query pagination nor the merged display limit may establish false uniqueness or hide a returned exact match.
- [C1] The ID query returns 50 substring-only nodes and the name query returns another node whose data.name exactly equals the requested short name. → Resolve the unique exact-name node instead of discarding it and reporting substring ambiguity.
- [C2] The ID query fills the merged limit with substring-only nodes and the alternate-name query returns a different node with an exact alternate name. → Resolve the exact-alias node instead of dropping it.
- [C3] The retained 50 ID matches include one exact ID, while a later name-query result on a different node is also an exact match. → Report multiple exact matches instead of silently selecting the retained exact-ID node.
- [C4] The retained candidates contain one exact match while a later alternate-name-query result on another node has an exact alias. → Report ambiguity rather than treating the retained exact match as unique.
- [C5] A field has more than 50 substring matches and its unique exact match falls beyond the first API page. → Find the exact match independently of the first substring page, such as through exact-match queries before bounded fallback.
- [C6] A complete lookup has one exact match, one nonexact match only, or multiple exact/nonexact matches. → Preserve the existing intended branches: choose a unique exact match, choose the sole nonexact match, and reject ambiguous results.
- [C7] A complete lookup returns no matches. → Retain the no-service error rather than introducing a fallback selection.
- [C8] A fully qualified composite ID is supplied. → Keep the strict-ID bypass without requiring name lookup or pagination.
Full review
F1 (high confidence): The backend only adds surrounding % wildcards when a LIKE value contains neither _ nor %. Passing payments_api unchanged therefore misses longer IDs/names and even an exact alias stored as ["payments_api"]. Both service find and short-name resolution can report no match. Construct the substring pattern explicitly rather than relying on server auto-wrapping.
F2 (high confidence): Results are merged ID-first and truncated to 50 before exact matching. With 50 ID substring hits, an exact name returned by the second query is discarded. Worse, retaining one exact match while discarding another makes an ambiguous name resolve silently. Establish exact matches and ambiguity before applying the display cap, using exact queries so API pagination cannot hide them either.
The graph only wraps a LIKE value in % when it holds no _ or %, so a query like payments_api was matched as a whole value. Send the pattern explicitly and drop the extra rows the _ wildcard lets through. Short-name resolution now queries exact id, name and alias matches on their own before the substring search, so a page of partial matches can neither hide the exact one nor make a shared name look unique. A full substring page counts as ambiguous. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Both findings were real; fixed in 3712ea7. F1 (underscores). Confirmed in nominal's F2 (truncation).
The display cap in
|
There was a problem hiding this comment.
Changes requested: 1 issue
- 🟡 Minor ·
src/commands/service/id.ts:88
Queries containing underscores miss matching service names and aliases.
Reviewed at 3712ea7.
For agents
Rationale: Exact-match truncation is fixed, but underscore lookups can still lose real matches when wildcard-only hits fill the first API page.
| Severity | Finding | Where |
|---|---|---|
| minor | review:5536204904:F1 Queries containing underscores miss matching service names and aliases. | src/commands/service/id.ts:88 |
review:5536204904:F1 invariant: An underscore in the query must not disable substring matching across service IDs, names and alternate names, whether finding services or resolving short names.
- [C1] Unscoped service find payments_api with a node whose id is prod-payments_api-worker and whose other name fields do not match. → Return the node because its ID contains the query.
- [C2] Provider-scoped service find payments_api with a node whose data.name is prod-payments_api-worker. → Return the matching node within the requested provider.
- [C3] Type-scoped service find payments_api with a node whose alternateNames contains payments_api and whose ID and data.name are unrelated. → Return the node within the requested type, despite alternate names being stored as a JSON array.
- [C4] Service find payments_api with both provider and type supplied and a matching alternate name. → Return the matching node while preserving both scope filters.
- [C5] Short-name resolution of payments_api for service show, logs, metrics or graph when the only matching resource has that alternate name. → Resolve that resource rather than reporting that no service exists.
- [C6] A fully qualified composite service ID containing underscores is supplied to a resolver caller. → Preserve the existing strict-ID bypass and do not perform substring queries.
- [C7] Unscoped service find payments_api when the ID query's first 50 raw LIKE hits have IDs prod-payments-api-0 through prod-payments-api-49, followed by prod-payments_api-worker; other name fields do not match. → Return prod-payments_api-worker; wildcard-only false positives must not consume the literal-result limit.
- [C8] Provider-scoped service find payments_api when 50 nodes in that provider have data.name values prod-payments-api-0 through prod-payments-api-49 before a node named prod-payments_api-worker; other fields do not match. → Return the literal name match while preserving the provider filter, rather than filtering the first page to an empty result.
- [C9] Type-scoped service find payments_api when the alternate_names query first returns 50 nodes with prod-payments-api-* aliases before a node with prod-payments_api-worker as an alias; IDs and data.name do not match. → Return the literal alias match within the requested type despite the preceding JSON-text LIKE false positives.
- [C10] Service find payments_api with both provider and type supplied, when 50 in-scope wildcard-only alias hits precede the only literal alias match. → Find the literal alias match beyond the first raw page and preserve both scope filters.
- [C11] Short-name resolution of payments_api for service show, logs, metrics or graph has no exact match, 50 wildcard-only ID hits on the first page, and the sole literal substring match prod-payments_api-worker on the next page. → Resolve the unique literal substring match after establishing completeness; do not report No service found from a fully filtered first page.
- [C12] Short-name resolution of payments_api has no exact match, 50 wildcard-only data.name hits before the sole literal substring name prod-payments_api-worker, and unrelated IDs and aliases. → Resolve the unique literal name match after paging the raw candidates instead of treating the filtered first page as complete.
- [C13] Short-name resolution of payments_api has no exact match, 50 wildcard-only alternate_names hits before the sole literal substring alias prod-payments_api-worker, and unrelated IDs and data.name. → Resolve the unique literal alias match after paging the raw candidates instead of reporting no service.
- [C14] findServiceNodes is called with query payments_api and limit 1, and its first raw ID hit is prod-payments-api-worker while the next is prod-payments_api-worker. → Apply the requested limit to literal matches, returning prod-payments_api-worker rather than an empty list.
| Finding | Case | Outcome | Current-head evidence |
|---|---|---|---|
| review:5536204904:F1 | C1 | fixed | At this head id.ts:105-108 sends %payments_api% and :119-126 keeps prod-payments_api-worker by literal inclusion. The original unscoped sparse ID scenario is repaired; raw-page saturation is covered by appended cases. |
| review:5536204904:F1 | C2 | fixed | id.ts:101-115 retains provider in filters for every field query; %payments_api% matches a longer data.name, and containsQuery at :81-84 retains it in the original sparse scenario. |
| review:5536204904:F1 | C3 | fixed | id.ts sends a type-scoped alternate_names LIKE %payments_api%; nominal generateClause applies LIKE to JSON text, so ["payments_api"] matches, and containsQuery retains its literal alias. |
| review:5536204904:F1 | C4 | fixed | id.ts:101-115 carries provider and type together in all three requests. The wrapped substring pattern and literal alias filter preserve the original matching-alias case without dropping either scope. |
| review:5536204904:F1 | C5 | fixed | id.ts:163-165 now queries exact matches first. nominal generateClause uses json_each for alternate_names '=', so an exact payments_api alias yields the single node. All four callers use resolveServiceId; test/service-find.test.ts also contains this alias fixture. |
| review:5536204904:F1 | C6 | fixed | id.ts:158-159 still returns parseServiceIdStrict(raw) before either lookup. The full-ID test now uses pay_api and asserts no API requests; show/logs/metrics/graph all retain this path. |
| review:5536204904:F2 | C1 | fixed | id.ts:163 queries '=' independently across fields before any substring lookup. Fifty ID substring-only nodes cannot fill the name '=' query or hide its unique exact name. |
| review:5536204904:F2 | C2 | fixed | id.ts:163 exact lookup includes alternate_names '='; backend json_each performs element equality. ID substring-only nodes never enter the exact candidate merge, so the exact alias is retained. |
| review:5536204904:F2 | C3 | fixed | lookupNodes merges all exact field results without a merged slice at :119-128; resolveServiceId:165 rejects multiple nodes. An exact ID and another node's exact name therefore remain ambiguous. |
| review:5536204904:F2 | C4 | fixed | Exact ID/name and exact-alias nodes are all merged before resolveServiceId:165 checks ambiguity; alternate_names '=' uses backend json_each. The display cap only exists in findServiceNodes:139, outside resolver exact selection. |
| review:5536204904:F2 | C5 | fixed | The '=' queries at id.ts:163 are independent of all LIKE pages; substring-only candidates do not consume an exact-query page. test/service-find.test.ts:113-120 supplies 60 partial matches before exact name/alias fixtures. |
| review:5536204904:F2 | C6 | fixed | id.ts:164 chooses one exact node, :165 rejects multiple exact nodes, and :176 selects a sole nonexact node only for an untruncated complete fallback. Other nonempty fallbacks reach multipleMatches. New F1 cases address incomplete wildcard-only fallback pages, not this complete-lookup case. |
| review:5536204904:F2 | C7 | fixed | When exact and complete substring queries have no matches, id.ts:168-174 still throws the no-service CLIError. No fallback node is synthesized. |
| review:5536204904:F2 | C8 | fixed | id.ts:158-159 preserves strict composite-ID return before name queries or pagination; all four resolver callers use this shared implementation. |
| Prior finding | Resolution | Evidence at this head |
|---|---|---|
| review:5536204904:F2 | fixed | Verified all C1-C8 against id.ts:93-177 and show/logs/metrics/graph callers. Independent '=' field queries and an uncapped exact merge establish exact preference and ambiguity before substring pagination; complete fallback uniqueness, no-match errors and strict-ID bypass remain intact. Backend do.ts confirms alternate_names equality uses json_each. Verification was by code inspection; no project tests were run. |
Full review
review:5536204904:F1 — Minor, high confidence: At src/commands/service/id.ts:108–124, the 50-row API limit applies before literal filtering. For payments_api, 50 prod-payments-api-* rows can consume the page because _ is a LIKE wildcard, hiding a later prod-payments_api-worker. service find then returns nothing; short-name resolution with no exact match reports “No service found.” The same failure affects names and aliases. Page through raw candidates before applying the literal-result cap or deciding uniqueness, and add a regression case with a full page of wildcard-only hits.
The _ in a query like payments_api matches any character, so rows such as prod-payments-api-N can fill a whole page and hide the literal match behind them. Each field query now reads pages of 200, ordered by id, until it has `limit` literal matches or runs out (10 pages at most, and a field that hits the cap counts as truncated). The limit applies to literal matches, not raw rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Follow-up on the reopened F1 (C7–C14), fixed in 7cb01cd. You're right that filtering only the first page wasn't enough: rows that match only because of the New tests put 250 wildcard-only rows ahead of the literal match for id, name and alias, both for |
| const { nodes } = await lookupNodes(api, workspaceId, query, 'substring', opts); | ||
| return nodes.slice(0, opts.limit ?? DEFAULT_FIND_LIMIT); |
There was a problem hiding this comment.
The truncation flag is computed and then dropped, so a capped listing looks complete; a stderr warning keeps stdout pure data.
| const { nodes } = await lookupNodes(api, workspaceId, query, 'substring', opts); | |
| return nodes.slice(0, opts.limit ?? DEFAULT_FIND_LIMIT); | |
| const { nodes, truncated } = await lookupNodes(api, workspaceId, query, 'substring', opts); | |
| if (truncated) process.stderr.write(`More nodes match "${query}" than the ${opts.limit ?? DEFAULT_FIND_LIMIT} shown. Narrow the query.\n`); | |
| return nodes.slice(0, opts.limit ?? DEFAULT_FIND_LIMIT); |
There was a problem hiding this comment.
Applied in 2229d3e, slightly reshaped: findServiceNodes now returns { nodes, truncated } (also true when merged literal matches exceed the limit), and the service find command writes the note to stderr, skipped under --quiet, so the library stays free of output. Test: says when more services match than it returns.
findServiceNodes now returns whether more services match than it returned, and service find notes it on stderr (unless --quiet), so a capped list no longer looks complete and stdout stays parseable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Changes requested: 1 issue
- 🟡 Minor ·
src/commands/service/id.ts:88
Queries containing underscores miss matching service names and aliases.
Reviewed at 2229d3e.
For agents
Rationale: The new warning helps nonquiet service find, but capped empty scans still produce false no-service errors and silently empty quiet JSON output.
| Severity | Finding | Where |
|---|---|---|
| minor | review:5536204904:F1 Queries containing underscores miss matching service names and aliases. | src/commands/service/id.ts:88 |
review:5536204904:F1 invariant: An underscore in the query must not disable substring matching across service IDs, names and alternate names, whether finding services or resolving short names.
- [C1] Unscoped service find payments_api with a node whose id is prod-payments_api-worker and whose other name fields do not match. → Return the node because its ID contains the query.
- [C2] Provider-scoped service find payments_api with a node whose data.name is prod-payments_api-worker. → Return the matching node within the requested provider.
- [C3] Type-scoped service find payments_api with a node whose alternateNames contains payments_api and whose ID and data.name are unrelated. → Return the node within the requested type, despite alternate names being stored as a JSON array.
- [C4] Service find payments_api with both provider and type supplied and a matching alternate name. → Return the matching node while preserving both scope filters.
- [C5] Short-name resolution of payments_api for service show, logs, metrics or graph when the only matching resource has that alternate name. → Resolve that resource rather than reporting that no service exists.
- [C6] A fully qualified composite service ID containing underscores is supplied to a resolver caller. → Preserve the existing strict-ID bypass and do not perform substring queries.
- [C7] Unscoped service find payments_api when the ID query's first 50 raw LIKE hits have IDs prod-payments-api-0 through prod-payments-api-49, followed by prod-payments_api-worker; other name fields do not match. → Return prod-payments_api-worker; wildcard-only false positives must not consume the literal-result limit.
- [C8] Provider-scoped service find payments_api when 50 nodes in that provider have data.name values prod-payments-api-0 through prod-payments-api-49 before a node named prod-payments_api-worker; other fields do not match. → Return the literal name match while preserving the provider filter, rather than filtering the first page to an empty result.
- [C9] Type-scoped service find payments_api when the alternate_names query first returns 50 nodes with prod-payments-api-* aliases before a node with prod-payments_api-worker as an alias; IDs and data.name do not match. → Return the literal alias match within the requested type despite the preceding JSON-text LIKE false positives.
- [C10] Service find payments_api with both provider and type supplied, when 50 in-scope wildcard-only alias hits precede the only literal alias match. → Find the literal alias match beyond the first raw page and preserve both scope filters.
- [C11] Short-name resolution of payments_api for service show, logs, metrics or graph has no exact match, 50 wildcard-only ID hits on the first page, and the sole literal substring match prod-payments_api-worker on the next page. → Resolve the unique literal substring match after establishing completeness; do not report No service found from a fully filtered first page.
- [C12] Short-name resolution of payments_api has no exact match, 50 wildcard-only data.name hits before the sole literal substring name prod-payments_api-worker, and unrelated IDs and aliases. → Resolve the unique literal name match after paging the raw candidates instead of treating the filtered first page as complete.
- [C13] Short-name resolution of payments_api has no exact match, 50 wildcard-only alternate_names hits before the sole literal substring alias prod-payments_api-worker, and unrelated IDs and data.name. → Resolve the unique literal alias match after paging the raw candidates instead of reporting no service.
- [C14] findServiceNodes is called with query payments_api and limit 1, and its first raw ID hit is prod-payments-api-worker while the next is prod-payments_api-worker. → Apply the requested limit to literal matches, returning prod-payments_api-worker rather than an empty list.
- [C15] Short-name resolution of payments_api for show, logs, metrics or graph has no exact match, 2000 wildcard-only ID matches zz-payments-api-0000 through zz-payments-api-1999, and the unique literal ID aa-payments_api-worker after the ten-page cap in backend DESC id order. → Resolve after establishing completeness, or explicitly report an incomplete lookup; do not claim No service found while the literal match remains unscanned.
- [C16] Short-name resolution of payments_api has no exact match; 2000 nodes zz-0000 through zz-1999 have wildcard-only data.name values prod-payments-api-N, and aa-target has the sole literal substring name prod-payments_api-worker beyond the ten-page DESC-ordered scan. IDs and aliases do not match. → Resolve after establishing completeness, or report an incomplete lookup rather than No service found.
- [C17] Short-name resolution of payments_api has no exact match; 2000 nodes zz-0000 through zz-1999 have wildcard-only aliases prod-payments-api-N, and aa-target has the sole literal substring alias prod-payments_api-worker beyond the ten-page DESC-ordered scan. IDs and data.name do not match. → Resolve after establishing completeness, or report an incomplete lookup rather than No service found.
- [C18] Unscoped service find payments_api --output json --quiet sees 2000 wildcard-only IDs zz-payments-api-0000 through zz-payments-api-1999 before aa-payments_api-worker under backend DESC id ordering; data.name and aliases do not match. → Return the literal match or an explicit machine-readable incomplete-search result/error; do not silently emit items [] and count 0 as a normal complete result.
- [C19] Provider-scoped service find payments_api --output json --quiet sees 2000 in-provider nodes zz-0000 through zz-1999 with wildcard-only data.name values prod-payments-api-N before aa-target with the literal name prod-payments_api-worker in DESC id order; IDs and aliases do not match. → Preserve the provider scope and return the literal match or surface the incomplete scan in machine-readable output/error, even when quiet suppresses progress.
- [C20] Type-scoped service find payments_api --output json --quiet sees 2000 in-type nodes zz-0000 through zz-1999 with wildcard-only aliases prod-payments-api-N before aa-target with literal alias prod-payments_api-worker in DESC id order; IDs and data.name do not match. → Preserve the type scope and return the literal alias match or expose that the capped search is incomplete rather than emitting a silent empty result.
- [C21] Service find payments_api --output json --quiet with both provider and type supplied sees 2000 in-scope wildcard-only alias hits on nodes zz-0000 through zz-1999 before the sole literal alias prod-payments_api-worker on aa-target in DESC id order. → Preserve both scope filters and return the literal match or an explicit incomplete-search result/error; quiet must not make capped empty scans indistinguishable from completed searches.
| Finding | Case | Outcome | Current-head evidence |
|---|---|---|---|
| review:5536204904:F1 | C1 | fixed | At 2229d3e service/id.ts:111-127 still sends %payments_api% and containsQuery at :81-84 retains prod-payments_api-worker. findServiceNodes:159-160 returns this node in its nodes property, consumed correctly by find.ts:27. |
| review:5536204904:F1 | C2 | fixed | service/id.ts:107-109 and :123 retain provider on every field/page request. Wrapped LIKE plus literal filtering still retains the original longer data.name match; the new NodeLookup return does not drop it. |
| review:5536204904:F1 | C3 | fixed | service/id.ts still queries alternate_names with %payments_api% and the type filter. Backend JSON-text LIKE admits the exact alias, and containsQuery retains it in the returned nodes property. |
| review:5536204904:F1 | C4 | fixed | service/id.ts:107-109 passes provider and type together through :123 on every request. Literal alias matching and the new findServiceNodes result preserve the original scoped match. |
| review:5536204904:F1 | C5 | fixed | service/id.ts:184-186 performs exact lookup before substring scans; alternate_names equality uses backend json_each. All four checked callers show/logs/metrics/graph use the same resolver, so the sole exact payments_api alias resolves. |
| review:5536204904:F1 | C6 | fixed | service/id.ts:179-180 returns strict composite IDs before lookup. The current test at service-find.test.ts:157-162 retains the pay_api fixture and zero-request assertion; all four resolver callers preserve this route. |
| review:5536204904:F1 | C7 | fixed | service/id.ts:119-131 scans 200 raw rows per page and counts only kept matches. Fifty wildcard-only ID hits plus the literal hit do not exhaust the scan. The updated test at service-find.test.ts:115-119 retains its 250-hit regression. |
| review:5536204904:F1 | C8 | fixed | The name scan at service/id.ts:117-134 retains provider per page, filters wildcard-only hits and completes the original 51-row scenario within one raw page, returning the literal node through findServiceNodes.nodes. |
| review:5536204904:F1 | C9 | fixed | The alternate_names scan at service/id.ts:117-134 retains type and does not count false positives toward the literal limit. Fifty false hits followed by the literal alias return that alias node. |
| review:5536204904:F1 | C10 | fixed | service/id.ts:107-109/:123 carries both filters through the alias scan, and :126-131 keeps the literal alias despite preceding 50 false hits. The scoped 250-hit test retains its assertion at service-find.test.ts:121-134. |
| review:5536204904:F1 | C11 | fixed | For the original 50-wildcard-ID-hit scenario, empty exact queries are followed by one complete 51-row partial page with one kept node and truncated false. service/id.ts:197 therefore resolves the literal ID. |
| review:5536204904:F1 | C12 | fixed | The data.name field scan filters the original 50 wildcard-only rows and retains the sole literal name on a short 51-row page. The complete one-node fallback resolves at service/id.ts:197. |
| review:5536204904:F1 | C13 | fixed | The alternate_names field scan filters the original 50 JSON-text LIKE false positives, retains the literal alias, and establishes completeness from the short raw page; service/id.ts:197 resolves it. |
| review:5536204904:F1 | C14 | fixed | service/id.ts:126-129 applies limit 1 after literal filtering, so the wildcard-only first row does not consume it. The updated helper returns the literal node plus truncation metadata at :160; service-find.test.ts:136-140 preserves the node assertion. |
Full review
review:5536204904:F1 — Minor, high confidence: The new warning only partially fixes scan-limit handling. With 2,000 wildcard-only IDs such as zz-payments-api-* sorting ahead of aa-payments_api-worker, lookup still returns { nodes: [], truncated: true }. The resolver ignores truncation at src/commands/service/id.ts:189 and reports “No service found” for show, logs, metrics and graph. Also, service find --output json --quiet suppresses the warning and returns {"items":[],"count":0} without an incompleteness signal. Preserve the cap, but surface incomplete scans in resolver errors and machine-readable output.
When the capped scan keeps no literal match, short-name resolution now says the lookup couldn't finish (with the full form and scoped find as next steps) instead of "No service found". service find's JSON output carries `truncated`, so a quiet, machine-read empty result is not mistaken for a complete one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
C15–C21 addressed in 8ae156d, keeping the 10-page cap as you suggested.
Tests: a capped scan (2,001 wildcard-only rows ahead of the literal) for id, name and alias reports the incomplete lookup, and a scoped capped find returns |
There was a problem hiding this comment.
LGTM: no issues found
Reviewed at 8ae156d.
For agents
Rationale: All 21 outstanding lookup cases are fixed at this head; no further actionable defects found.
| Finding | Case | Outcome | Current-head evidence |
|---|---|---|---|
| review:5536204904:F1 | C1 | fixed | service/id.ts:111-129 sends %payments_api% and applies literal containsQuery before counting matches. The original sparse ID scenario returns prod-payments_api-worker through findServiceNodes.nodes. |
| review:5536204904:F1 | C2 | fixed | service/id.ts:107-123 retains provider on every field/page request; wrapped LIKE plus containsQuery retains the original longer data.name match. service/find.ts:27 consumes the returned nodes. |
| review:5536204904:F1 | C3 | fixed | service/id.ts queries alternate_names with %payments_api% while retaining type. JSON-text LIKE admits the original exact alias, and containsQuery retains it in the returned nodes. |
| review:5536204904:F1 | C4 | fixed | service/id.ts:107-123 carries provider and type together on every query. Wrapped alias matching plus literal filtering preserves the original matching alias without dropping either scope. |
| review:5536204904:F1 | C5 | fixed | service/id.ts:184-186 queries exact fields first, including alternate_names equality, and returns the sole exact alias node. Checked show.ts, logs.ts, metrics.ts and graph.ts: all use this resolver before their resource requests. |
| review:5536204904:F1 | C6 | fixed | service/id.ts:179-180 returns parseServiceIdStrict(raw) before lookup. All four resolver callers retain this route; service-find.test.ts:182-187 uses pay_api and asserts zero API requests. |
| review:5536204904:F1 | C7 | fixed | service/id.ts:119-131 scans 200 raw rows per page and counts only literal matches. The original 50 wildcard-only IDs plus one literal ID fit in a complete page and return the literal node; the 250-hit regression fixture also exercises continued paging. |
| review:5536204904:F1 | C8 | fixed | service/id.ts:117-134 scans the name field with provider retained on every page and filters wildcard-only rows before counting. The original 51-row scenario returns the sole literal name match. |
| review:5536204904:F1 | C9 | fixed | The alternate_names scan at service/id.ts:117-134 retains type and ignores wildcard-only aliases for the literal limit. Fifty false positives followed by the original literal alias are returned on a complete page. |
| review:5536204904:F1 | C10 | fixed | service/id.ts:107-123 carries provider and type through every alias page, and :126-129 counts only kept matches. The original literal alias beyond 50 false hits is retained; the scoped 250-hit fixture checks continued paging and both request filters. |
| review:5536204904:F1 | C11 | fixed | With empty exact queries, the original 50 wildcard-only ID rows and sole literal ID produce one node on a short substring page with truncated false. service/id.ts:206 resolves that node; all four command callers share this path. |
| review:5536204904:F1 | C12 | fixed | The data.name scan filters the original 50 false positives and retains the sole longer literal name on a complete 51-row page. service/id.ts:206 permits the resulting unique, untruncated fallback. |
| review:5536204904:F1 | C13 | fixed | The alternate_names scan filters the original 50 JSON-text LIKE false positives, retains the sole literal alias and establishes completeness on a short page. service/id.ts:206 then resolves it. |
| review:5536204904:F1 | C14 | fixed | service/id.ts:126-129 checks keep before incrementing nodes.length, so a wildcard-only first row cannot consume limit 1. findServiceNodes slices the retained literal nodes; service-find.test.ts:136-140 preserves the explicit result assertion. |
| review:5536204904:F1 | C15 | fixed | After ten full ID pages containing only wildcard false positives, service/id.ts:134 returns truncated true with zero nodes. The new :190-197 branch throws an incomplete-lookup CLIError before the no-service branch. This is independent of ascending or descending backend order and reaches all four callers. |
| review:5536204904:F1 | C16 | fixed | A capped data.name scan propagates truncated through service/id.ts:145 even when it keeps no nodes. The new zero-node/truncated branch at :190-197 reports incomplete lookup rather than No service found; the capped name fixture checks that error branch. |
| review:5536204904:F1 | C17 | fixed | A capped alternate_names scan propagates truncated even when literal alias filtering keeps nothing. service/id.ts:190-197 now reports incomplete lookup before :199-205 can claim no service; the capped alias fixture covers this branch. |
| review:5536204904:F1 | C18 | fixed | An unscoped capped ID scan produces nodes [] and truncated true. service/find.ts:37-40 now includes truncated in its result, and output/formatter.ts:81-83 serializes the entire result before any quiet handling, preserving the machine-readable incompleteness flag. |
| review:5536204904:F1 | C19 | fixed | Provider is retained by service/id.ts:107-123 on every data.name page. Any capped field sets the merged truncated flag, passed through findServiceNodes and service/find.ts:40 into JSON; quiet only suppresses the later stderr warning. |
| review:5536204904:F1 | C20 | fixed | Type remains on every alternate_names request. service/id.ts:145/:160 preserves capped-scan truncation, service/find.ts:40 includes it, and formatList JSON output retains it independently of quiet, so an empty capped alias search no longer looks complete. |
| review:5536204904:F1 | C21 | fixed | Both provider and type remain in filters on every alias page. Truncation is ORed across fields and included in service find JSON before quiet handling; the scoped capped-find fixture also asserts nodes [] and truncated true. |
| Prior finding | Resolution | Evidence at this head |
|---|---|---|
| review:5536204904:F1 | fixed | Verified C1-C21 at 8ae156d by inspecting service/id.ts, service/find.ts, output/formatter.ts, output/json.ts, errors/handler.ts and all show/logs/metrics/graph resolver callers. Wrapped literal-aware paged queries and scope propagation preserve C1-C14. New empty-truncated handling reports incomplete lookup for C15-C17; JSON now preserves truncated even with quiet for C18-C21. Reviewed existing and added regression fixtures without executing project tests. The fix is order-independent and covers all three queried fields and all four scope combinations. |
Full review
The prior lookup issue is fixed; no further actionable findings.
The CLI builds against the live API again:
memoryreads the new git-backed memory API,service findand name resolution use the node list filter, and thefeedandnotecommands go. Without this, CI fails on every CLI PR and those commands error for users.Why: nominal removed node search (#4921) and the old feed, notes and memory endpoints (#4745 and earlier) on 2026-10-15. The CLI generates its client from the live spec, so typecheck broke on main.
Where to look
service show/logs/metrics/graph: exact matches first, then a sole untruncated substring match; a capped scan that kept nothing is reported as incomplete, never "not found".memory showandmemory historyread a file and the commit log.truncatedin its JSON and notes it on stderr, so a capped list never passes for a complete one.Feedback wanted: Is a substring match good enough for
service find, or should the CLI call something smarter now the vector search is gone?Risk: Users of
feed,note,memory save/delete/findlose those commands, but they already fail against production. Roll back by revert (main would go red again).Verified:
npm run typecheck,npm run lintandnpm test(584 pass, offline) against the current production spec.Decisions (3)
findResourcesdoes the same split.For agents
The four commits don't typecheck individually because the API removals break several command groups at once; review the branch as a whole.
src/generated/is gitignored and regenerated from the live spec by the typecheck step.committedAtfrom memories.commits is in seconds (as nominal's console reads it), somemory historymultiplies by 1000.🤖 Generated with Claude Code