From c5bbe58cf6e83829232ec3729428d813fc3e164e Mon Sep 17 00:00:00 2001 From: Juha Litola Date: Tue, 21 Apr 2026 19:23:47 +0300 Subject: [PATCH 1/2] refactor(code-nav): consume codeIndexState, drop deprecated indexingStatus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The backend deprecated `indexingStatus: IndexingStatus` in favour of `codeIndexState: CodeIndexState` across every code-navigation result type (searchSymbols, listRepoFiles, fetchCodeContext, grepRepoFile, plus several we don't consume). Legacy field still served. Defensive migration ahead of removal, same shape as the `pkg deps` typed-field swap in PR #22. The enums are near-identical on the wire: - `INDEXED` → `CURRENT` (same happy-path semantics). - `INDEXED` → `STALE` is a new lifecycle state — results were served but the navpack is older than VersionManifest and a reindex is enqueued. We treat it as happy-path, same as `CURRENT` / legacy `INDEXED`. - `INDEXING` / `PENDING` / `FAILED` / `NOT_FOUND` / `UNRESOLVABLE` values are unchanged. Our sentinel checks (`=== "INDEXING"` and `=== "UNRESOLVABLE"`) continue to fire on the same string values; string comparison logic is unchanged. Changes: - All four GraphQL queries select `codeIndexState` instead of `indexingStatus`. Zod schemas renamed accordingly. - `throwIfIndexing` helper and `searchSymbols` inline sentinel swap to reading `codeIndexState`. All consumers still gated on the same values. - Test fixtures migrate from `indexingStatus: "INDEXED"` → `codeIndexState: "CURRENT"`; `indexingStatus: "INDEXING"` → `codeIndexState: "INDEXING"`. - Doc comments on the response-builder modules updated to reference the new field name. No envelope shape change. `codeIndexState` is consumed internally only — never surfaced on the `list_files` / `read_file` / `grep_file` / `search_symbols` envelopes. `STALE` results served today flow through identically (we never branched on the absence of a staleness indicator); future UX work could surface an "index being refreshed" note in verbose mode, but that's out of scope. Live-smoke: all four indexing-gated tools (`code files`, `code read`, `code grep`, `code search`) verified against production pkgseer on `npm:express`. CURRENT state rendered as happy-path; INDEXING retry (`code search … --wait 30`) still promotes to the typed error and resolves on retry. --- src/services/code-navigation-service.test.ts | 32 ++++++++++---------- src/services/code-navigation-service.ts | 30 +++++++++--------- src/shared/grep-file-response.ts | 2 +- src/shared/list-files-response.ts | 2 +- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/services/code-navigation-service.test.ts b/src/services/code-navigation-service.test.ts index c97af25c..8f23836c 100644 --- a/src/services/code-navigation-service.test.ts +++ b/src/services/code-navigation-service.test.ts @@ -55,7 +55,7 @@ describe("CodeNavigationServiceImpl", () => { indexedVersion: "4.18.0", diagnostics: { hint: null }, warning: null, - indexingStatus: "INDEXED", + codeIndexState: "CURRENT", }, }, }), @@ -106,7 +106,7 @@ describe("CodeNavigationServiceImpl", () => { indexedVersion: null, diagnostics: { hint: null }, warning: null, - indexingStatus: "INDEXED", + codeIndexState: "CURRENT", }, }, }), @@ -152,7 +152,7 @@ describe("CodeNavigationServiceImpl", () => { indexedVersion: null, diagnostics: { hint: null }, warning: null, - indexingStatus: "INDEXING", + codeIndexState: "INDEXING", indexingRef: "idx-123", availableVersions: [{ version: "4.18.0", ref: "v4.18.0" }], }, @@ -225,7 +225,7 @@ describe("CodeNavigationServiceImpl", () => { indexedVersion: null, diagnostics: { hint: null }, warning: null, - indexingStatus: "INDEXED", + codeIndexState: "CURRENT", }, }, }), @@ -604,7 +604,7 @@ describe("CodeNavigationServiceImpl", () => { indexedVersion: null, diagnostics: { hint: null }, warning: null, - indexingStatus: "INDEXING", + codeIndexState: "INDEXING", indexingRef: "idx-123", }, }, @@ -698,7 +698,7 @@ describe("CodeNavigationServiceImpl", () => { indexedVersion: null, diagnostics: { hint: null }, warning: null, - indexingStatus: "INDEXED", + codeIndexState: "CURRENT", }, }, }), @@ -756,7 +756,7 @@ describe("CodeNavigationServiceImpl", () => { commitSha: "abc123", }, diagnostics: null, - indexingStatus: "INDEXED", + codeIndexState: "CURRENT", }, }, }), @@ -802,7 +802,7 @@ describe("CodeNavigationServiceImpl", () => { indexedVersion: null, resolution: null, diagnostics: null, - indexingStatus: "INDEXING", + codeIndexState: "INDEXING", indexingRef: "ref_xyz", availableVersions: [{ version: "4.21.0", ref: "v4.21.0" }], }, @@ -845,7 +845,7 @@ describe("CodeNavigationServiceImpl", () => { indexedVersion: "v5.2.1", resolution: null, diagnostics: { hint: "No files match that prefix." }, - indexingStatus: "INDEXED", + codeIndexState: "CURRENT", }, }, }), @@ -885,7 +885,7 @@ describe("CodeNavigationServiceImpl", () => { startLine: 1, endLine: 2, isBinary: false, - indexingStatus: "INDEXED", + codeIndexState: "CURRENT", }, }, }), @@ -920,7 +920,7 @@ describe("CodeNavigationServiceImpl", () => { startLine: null, endLine: null, isBinary: true, - indexingStatus: "INDEXED", + codeIndexState: "CURRENT", }, }, }), @@ -950,7 +950,7 @@ describe("CodeNavigationServiceImpl", () => { content: null, filePath: null, language: null, - indexingStatus: "INDEXING", + codeIndexState: "INDEXING", indexingRef: "ref_read", }, }, @@ -1044,7 +1044,7 @@ describe("CodeNavigationServiceImpl", () => { commitSha: "abc", }, diagnostics: null, - indexingStatus: "INDEXED", + codeIndexState: "CURRENT", }, }, }), @@ -1078,7 +1078,7 @@ describe("CodeNavigationServiceImpl", () => { matches: [], totalMatches: 0, hasMore: false, - indexingStatus: "INDEXING", + codeIndexState: "INDEXING", indexingRef: "ref_grep", availableVersions: [{ version: "4.21.0", ref: "v4.21.0" }], }, @@ -1117,7 +1117,7 @@ describe("CodeNavigationServiceImpl", () => { matches: [], totalMatches: 0, hasMore: false, - indexingStatus: "INDEXED", + codeIndexState: "CURRENT", }, }, }), @@ -1161,7 +1161,7 @@ describe("CodeNavigationServiceImpl", () => { files: [], total: 0, hasMore: false, - indexingStatus: "INDEXED", + codeIndexState: "CURRENT", }, }, }), diff --git a/src/services/code-navigation-service.ts b/src/services/code-navigation-service.ts index 4fa2874d..6d826721 100644 --- a/src/services/code-navigation-service.ts +++ b/src/services/code-navigation-service.ts @@ -477,7 +477,7 @@ query SearchSymbols( hint } warning - indexingStatus + codeIndexState indexingRef availableVersions { version @@ -529,7 +529,7 @@ const searchSymbolsResponseSchema = z.object({ .nullable() .optional(), warning: z.string().nullable().optional(), - indexingStatus: z.string(), + codeIndexState: z.string(), indexingRef: z.string().nullable().optional(), availableVersions: z.array(availableVersionSchema).nullable().optional(), }); @@ -542,7 +542,7 @@ const graphQLErrorSchema = z.object({ // -------------------------------------------------------------------- // Zod schemas + queries for the file-exploration bundle. // `listRepoFiles` / `fetchCodeContext` / `grepRepoFile` share the same -// indexing lifecycle (`indexingStatus` + `indexingRef` + +// indexing lifecycle (`codeIndexState` + `indexingRef` + // `availableVersions`) but otherwise have distinct result shapes — // normalise per tool rather than under one abstraction. // -------------------------------------------------------------------- @@ -581,7 +581,7 @@ const listRepoFilesResponseSchema = z.object({ indexedVersion: z.string().nullable().optional(), resolution: navigationResolutionSchema, diagnostics: navigationDiagnosticsSchema, - indexingStatus: z.string(), + codeIndexState: z.string(), indexingRef: z.string().nullable().optional(), availableVersions: z.array(availableVersionSchema).nullable().optional(), }); @@ -636,7 +636,7 @@ query ListRepoFiles( diagnostics { hint } - indexingStatus + codeIndexState indexingRef availableVersions { version @@ -659,7 +659,7 @@ const codeContextResponseSchema = z.object({ repoUrl: z.string().nullable().optional(), gitRef: z.string().nullable().optional(), isBinary: z.boolean().nullable().optional(), - indexingStatus: z.string(), + codeIndexState: z.string(), indexingRef: z.string().nullable().optional(), }); @@ -705,7 +705,7 @@ query FetchCodeContext( repoUrl gitRef isBinary - indexingStatus + codeIndexState indexingRef } }`; @@ -729,7 +729,7 @@ const grepRepoFileResponseSchema = z.object({ indexedVersion: z.string().nullable().optional(), resolution: navigationResolutionSchema, diagnostics: navigationDiagnosticsSchema, - indexingStatus: z.string(), + codeIndexState: z.string(), indexingRef: z.string().nullable().optional(), availableVersions: z.array(availableVersionSchema).nullable().optional(), }); @@ -790,7 +790,7 @@ query GrepRepoFile( diagnostics { hint } - indexingStatus + codeIndexState indexingRef availableVersions { version @@ -901,7 +901,7 @@ export class CodeNavigationServiceImpl implements CodeNavigationService { ); } - if (data.indexingStatus === "INDEXING") { + if (data.codeIndexState === "INDEXING") { throw new CodeNavigationIndexingError( this.createIndexingMessage(data.indexingRef ?? undefined), data.indexingRef ?? undefined, @@ -912,7 +912,7 @@ export class CodeNavigationServiceImpl implements CodeNavigationService { ); } - if (data.indexingStatus === "UNRESOLVABLE") { + if (data.codeIndexState === "UNRESOLVABLE") { throw new CodeNavigationUnresolvableError( "The requested target or version could not be resolved.", ); @@ -1107,17 +1107,17 @@ export class CodeNavigationServiceImpl implements CodeNavigationService { /** * Shared sentinel-promotion for the file-exploration tools. When the backend - * response carries `indexingStatus: "INDEXING"` (data-path variant), + * response carries `codeIndexState: "INDEXING"` (data-path variant), * throw the typed error so the envelope builder / caller never sees * the raw sentinel. Mirrors the inline check `searchSymbols` does * today. */ private throwIfIndexing(data: { - indexingStatus: string; + codeIndexState: string; indexingRef?: string | null; availableVersions?: Array<{ version?: string | null; ref: string }> | null; }): void { - if (data.indexingStatus === "INDEXING") { + if (data.codeIndexState === "INDEXING") { throw new CodeNavigationIndexingError( this.createIndexingMessage(data.indexingRef ?? undefined), data.indexingRef ?? undefined, @@ -1296,7 +1296,7 @@ export class CodeNavigationServiceImpl implements CodeNavigationService { // `fetchCodeContext` doesn't return availableVersions; pass a // minimal object to the shared helper. this.throwIfIndexing({ - indexingStatus: data.indexingStatus, + codeIndexState: data.codeIndexState, indexingRef: data.indexingRef, }); diff --git a/src/shared/grep-file-response.ts b/src/shared/grep-file-response.ts index f1ed8d6b..7a31ae16 100644 --- a/src/shared/grep-file-response.ts +++ b/src/shared/grep-file-response.ts @@ -7,7 +7,7 @@ * `resolution` when backend returned one; `hint` when empty * results carry a backend diagnostic. * - **No indexing metadata in the success envelope.** Service - * promotes `indexingStatus: INDEXING` to a typed error first. + * promotes `codeIndexState: INDEXING` to a typed error first. * - **`filter.*` echoes only caller-supplied inputs.** * - **Regex-char heuristic on empty results (terminal-only).** If * the pattern looks like unambiguous regex AND zero matches, diff --git a/src/shared/list-files-response.ts b/src/shared/list-files-response.ts index 3b40a4b5..6d84938d 100644 --- a/src/shared/list-files-response.ts +++ b/src/shared/list-files-response.ts @@ -9,7 +9,7 @@ * `resolution` appears whenever the backend returned one; `hint` * appears when empty results carry a backend diagnostic. * - **No indexing metadata in the success envelope.** The service - * layer promotes `indexingStatus: INDEXING` to a typed error + * layer promotes `codeIndexState: INDEXING` to a typed error * before the envelope builder runs, so agents never branch on a * data-path indexing flag. * - **`filter.*` echoes only caller-supplied inputs.** The default From b453204fb26315830fc8e8fa693337e790bc5418 Mon Sep 17 00:00:00 2001 From: Juha Litola Date: Tue, 21 Apr 2026 19:39:12 +0300 Subject: [PATCH 2/2] docs(code-nav): update tools.md indexing-lifecycle reference to codeIndexState Follows the field rename landed in this PR's main commit. The tools.md paragraph described the success-sentinel shape as `indexingStatus`; updated to `codeIndexState: "INDEXING"` to match what the service layer actually reads off the wire now. --- docs/implementation/tools.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/implementation/tools.md b/docs/implementation/tools.md index 9b788ebb..c08d9982 100644 --- a/docs/implementation/tools.md +++ b/docs/implementation/tools.md @@ -121,7 +121,7 @@ These three indexing-gated tools share an addressing and lifecycle contract (doc ### Indexing lifecycle (shared across `search_symbols`, `list_files`, `read_file`, `grep_file`) -All four code-navigation tools share the same indexing-retry contract. The state can arrive through either an error response or a success sentinel, and the service layer collapses both to the same typed `CodeNavigationIndexingError` before the envelope builder runs. Agents therefore never see an `indexingStatus` field in a success envelope; they branch on the error path instead. +All four code-navigation tools share the same indexing-retry contract. The state can arrive through either an error response or a success sentinel (`codeIndexState: "INDEXING"`), and the service layer collapses both to the same typed `CodeNavigationIndexingError` before the envelope builder runs. Agents therefore never see a `codeIndexState` field in a success envelope; they branch on the error path instead. **`INDEXING` error envelope**: ```json