docs: tighten the display-text notes

Name routeId among the canonical analyst IDs in CURRENT-STATE, say what the
changelog entry changes for users, and drop the comment that repeated the
canonical-field doc.
This commit is contained in:
German Khokhlov committed 2026-11-10 15:34:32 +04:00
1 parent 3521c96172
commit 12a5630991
4 files changed
+15 -18

No files matched your search

+3 -1
View File
@@ -1 +1,3 @@
- Bound untrusted display text at output boundaries (asturksever, #1280). - Keep outside text in voice and MCP tool results to one line of 180
characters: ALPR tags, station names and other display fields (asturksever,
#1280).
+7 -10
View File
@@ -137,16 +137,13 @@ Stale fallback runs per response after the reservation is released, and disk
writes finish asynchronously. Settled and rejected reservations are removed writes finish asynchronously. Settled and rejected reservations are removed
only while they still own their map entry. only while they still own their map entry.
External ALPR tags, surveillance-tool display fields, voice context text and External ALPR tags, surveillance-tool fields, voice context text and compact
compact analyst strings collapse controls and whitespace before a final analyst strings become one line of at most 180 characters (scene labels: 120).
180-character cap (scene labels retain their 120-character cap). Empty ALPR Empty ALPR values stay null, and the cap never splits a surrogate pair.
values remain null and Unicode text is preserved without splitting a surrogate Canonical analyst identifiers such as `id` and `routeId` stay unchanged, because
pair. Only display fields are normalized: canonical analyst identifiers voice follow-ups match on them and bikeshare uses the raw station name as its
(`id`, `layerKey`, `icao24`, `mmsi`, `noradId`, `registration`, `callsign`) ID. The text is still untrusted: normalization does not prevent prompt
pass through unchanged, because bikeshare uses the raw station name as its ID injection.
and numbered voice references resolve by that key. These fields remain
untrusted source data in structured tool output; text normalization does not
establish trust or eliminate prompt injection.
Terrain height requests accept at most 64 points, matching the browser service's Terrain height requests accept at most 64 points, matching the browser service's
existing batch size and one bounded upstream chunk. Larger requests return HTTP existing batch size and one bounded upstream chunk. Larger requests return HTTP
+1 -1
View File
@@ -1,4 +1,4 @@
/** Bounded, single-line external display text; it remains untrusted source data. */ /** One-line text capped at `maxLength` without splitting a surrogate pair. Still untrusted. */
export function normalizeDisplayText(value, maxLength = 180) { export function normalizeDisplayText(value, maxLength = 180) {
if (value == null || typeof value === 'object' || typeof value === 'function') if (value == null || typeof value === 'object' || typeof value === 'function')
return ''; return '';
+4 -6
View File
@@ -5353,9 +5353,9 @@ const ANALYST_IDENTITY_FIELDS = Object.freeze([
]); ]);
/** /**
* Analyst item keys that identify a record rather than describe it: the * Keys that identify an analyst record. The referent registry, track_entity,
* referent registry, track_entity and locateLayerEntity match on them, so * locateLayerEntity and follow-up filters match on them, so they skip
* display-text normalization must leave them byte-for-byte unchanged. * display-text normalization.
*/ */
const ANALYST_CANONICAL_FIELDS = new Set([ const ANALYST_CANONICAL_FIELDS = new Set([
'layerKey', 'layerKey',
@@ -5405,9 +5405,7 @@ function compactAnalystItem(r) {
} else compact[key] = r[key]; } else compact[key] = r[key];
} }
if (present(r.ageHours)) compact.ageHours = r.ageHours; if (present(r.ageHours)) compact.ageHours = r.ageHours;
// Display text only: canonical keys are lookup identities (bikeshare uses // Canonical keys stay raw: bikeshare's id is the raw station name.
// the raw station name as its id), so the referent registry and
// locateLayerEntity must see them exactly as the layer reports them.
for (const key of Object.keys(compact)) { for (const key of Object.keys(compact)) {
if (typeof compact[key] === 'string' && !ANALYST_CANONICAL_FIELDS.has(key)) if (typeof compact[key] === 'string' && !ANALYST_CANONICAL_FIELDS.has(key))
compact[key] = normalizeDisplayText(compact[key]); compact[key] = normalizeDisplayText(compact[key]);