From 1af0bb5102632677b23e122ae613367d2d230871 Mon Sep 17 00:00:00 2001 From: KageBinary Date: Sat, 24 Oct 2026 15:01:40 -0700 Subject: [PATCH] fix(tools): point the stale-map line at ix map --remap The stale-map line said "a background refresh is running", which is not always true: Ix also reports a stale map when background refresh is off (IX_MAP_BACKGROUND_REFRESH=0), when the last refresh failed, or when it could not start. On the llm path the line also replaces Ix's own map_stale record, whose "Run ix map --remap" remedy was lost. The line now gives that remedy instead. Adds unit cases for two map_stale records, a record that is not the first line, and a 1-file map. Co-Authored-By: Claude Opus 5.5 --- TOOL_CONTRACT.md | 2 +- runtime/map-stale.ts | 10 ++++++++-- tests/map-stale.test.ts | 34 ++++++++++++++++++++++++++++++++-- tests/success-fixtures.test.ts | 4 ++-- 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/TOOL_CONTRACT.md b/TOOL_CONTRACT.md index 379bc96..96fbfc7 100644 --- a/TOOL_CONTRACT.md +++ b/TOOL_CONTRACT.md @@ -172,7 +172,7 @@ Risk levels: `low` → `SAFE TO PROCEED`, `medium` → `REVIEW CALLERS FIRST`, ` - Lists `scores[]` from `ix subsystems --list` as subsystem health - Takes the file count from `stats.nodes.byKind` (kind `file`) and totals from `nodes.total` / `edges.total` - Flags multi-file subsystems with low cohesion (< 0.4) with ⚠. Ix's `coupling` is an edge-weight count, not a 0–1 ratio, so it is shown but not judged -- When either `ix subsystems` call reports the architecture map stale (`map_stale: true`, Ix#880), adds one line under the heading: `_Map is stale (regions as of map rev 7, 42 files); recent edits may not be reflected — a background refresh is running._` (the parenthetical only when Ix gives `regions_as_of`). Without the field (fresh map, older CLI) the output is unchanged +- When either `ix subsystems` call reports the architecture map stale (`map_stale: true`, Ix#880), adds one line under the heading: `` _Map is stale (regions as of map rev 7, 42 files); recent edits may not be reflected — run `ix map --remap` to refresh it now._ `` (the parenthetical only when Ix gives `regions_as_of`). Without the field (fresh map, older CLI) the output is unchanged **Example output:** ``` diff --git a/runtime/map-stale.ts b/runtime/map-stale.ts index 9d38c5a..97450d4 100644 --- a/runtime/map-stale.ts +++ b/runtime/map-stale.ts @@ -79,10 +79,16 @@ export function splitLlmMapStale(text: string): { stale: MapStale | null; body: return { stale, body: kept.join("\n").trim() }; } -/** The one line a tool puts under its heading while the map is stale. */ +/** + * The one line a tool puts under its heading while the map is stale. It does + * not say a background refresh is under way: Ix also reports a stale map when + * background refresh is off (IX_MAP_BACKGROUND_REFRESH=0), failed or could + * not start. It carries the remedy Ix's own llm record gives, which the tool + * drops from the body. + */ export function mapStaleLine(stale: MapStale): string { const asOf = stale.regionsAsOf ? ` (regions as of map rev ${stale.regionsAsOf.map_rev}, ${stale.regionsAsOf.file_count} file${stale.regionsAsOf.file_count === 1 ? "" : "s"})` : ""; - return `_Map is stale${asOf}; recent edits may not be reflected — a background refresh is running._`; + return `_Map is stale${asOf}; recent edits may not be reflected — run \`ix map --remap\` to refresh it now._`; } diff --git a/tests/map-stale.test.ts b/tests/map-stale.test.ts index b77a86a..6645f1c 100644 --- a/tests/map-stale.test.ts +++ b/tests/map-stale.test.ts @@ -48,6 +48,24 @@ describe("splitLlmMapStale", () => { expect(body).toBe("map files=1"); }); + test("two records: both come out, the first one names the map", () => { + const second = 'diagnostic code=map_stale message="Stale map (map rev 9, 50 files)."'; + const { stale, body } = splitLlmMapStale(`${RECORD}\nmap files=42 regions=3\n${second}\nregion id=a label=X`); + expect(stale).toEqual({ regionsAsOf: { map_rev: 7, file_count: 42 } }); + expect(body).toBe("map files=42 regions=3\nregion id=a label=X"); + }); + + test("a record that is not the first line is still found and taken out", () => { + const { stale, body } = splitLlmMapStale(`map files=42 regions=3\n${RECORD}\nregion id=a label=X`); + expect(stale).toEqual({ regionsAsOf: { map_rev: 7, file_count: 42 } }); + expect(body).toBe("map files=42 regions=3\nregion id=a label=X"); + }); + + test("a 1-file map is read from the singular \"1 file\"", () => { + const { stale } = splitLlmMapStale('diagnostic code=map_stale message="Stale map (map rev 3, 1 file)."\nmap files=1'); + expect(stale).toEqual({ regionsAsOf: { map_rev: 3, file_count: 1 } }); + }); + test("text without the record comes back unchanged", () => { const text = "map files=1 regions=1\ndiagnostic code=other message=x"; expect(splitLlmMapStale(text)).toEqual({ stale: null, body: text }); @@ -61,10 +79,22 @@ describe("splitLlmMapStale", () => { describe("mapStaleLine", () => { test("names the map when known", () => { expect(mapStaleLine({ regionsAsOf: { map_rev: 7, file_count: 42 } })).toBe( - "_Map is stale (regions as of map rev 7, 42 files); recent edits may not be reflected — a background refresh is running._", + "_Map is stale (regions as of map rev 7, 42 files); recent edits may not be reflected — run `ix map --remap` to refresh it now._", ); expect(mapStaleLine({})).toBe( - "_Map is stale; recent edits may not be reflected — a background refresh is running._", + "_Map is stale; recent edits may not be reflected — run `ix map --remap` to refresh it now._", + ); + }); + + test("a 1-file map says \"1 file\"", () => { + expect(mapStaleLine({ regionsAsOf: { map_rev: 3, file_count: 1 } })).toBe( + "_Map is stale (regions as of map rev 3, 1 file); recent edits may not be reflected — run `ix map --remap` to refresh it now._", ); }); + + test("does not promise a background refresh", () => { + for (const s of [{}, { regionsAsOf: { map_rev: 7, file_count: 42 } }]) { + expect(mapStaleLine(s)).not.toContain("background refresh"); + } + }); }); diff --git a/tests/success-fixtures.test.ts b/tests/success-fixtures.test.ts index 4b526e4..1c0444c 100644 --- a/tests/success-fixtures.test.ts +++ b/tests/success-fixtures.test.ts @@ -593,8 +593,8 @@ describe("ix-smells on real output", () => { * added, as Ix's `mapStaleJson` / `mapStaleLlm` add them. */ describe("a stale architecture map is said once, under the heading", () => { - const STALE = "_Map is stale (regions as of map rev 1, 38 files); recent edits may not be reflected — a background refresh is running._"; - const STALE_NO_ASOF = "_Map is stale; recent edits may not be reflected — a background refresh is running._"; + const STALE = "_Map is stale (regions as of map rev 1, 38 files); recent edits may not be reflected — run `ix map --remap` to refresh it now._"; + const STALE_NO_ASOF = "_Map is stale; recent edits may not be reflected — run `ix map --remap` to refresh it now._"; const withoutLine = (output: string, line: string) => output.replace(`${line}\n\n`, ""); for (const llm of [false, true]) {