fix(tools): exclude unknown altitude from requested aircraft bands (#1115)

aircraft_in_area compared a nullable altitude, so a contact with neither barometric nor geometric altitude matched max_altitude_m as though it were at zero. Require a reported finite altitude only when a band is requested; unfiltered queries keep the contact.
This commit is contained in:
Rudy Mizrahi Celekli authored and GitHub committed 2026-10-16 14:53:50 -07:00
1 parent e5ee6696b1
commit fa8e3e989f
4 files changed
+66

No files matched your search

@@ -0,0 +1,2 @@
- Exclude aircraft without a reported altitude from requested altitude bands;
unfiltered aircraft queries still retain them (rudycelekli, #1115).
+3
View File
@@ -100,6 +100,9 @@ shared `area` argument (place name, bounding box, or point and radius) and cap
lists at 25 rows by default. Voice offers the same queries next to its app lists at 25 rows by default. Voice offers the same queries next to its app
actions: the session lists them, and the browser runs them through the same actions: the session lists them, and the browser runs them through the same
catalog, loaded on first use. See [tools and the MCP server](TOOLS.md). catalog, loaded on first use. See [tools and the MCP server](TOOLS.md).
Aircraft altitude-band queries require a reported barometric or geometric altitude
for each match, so a grounded contact that reports none is left out of a band too.
Queries without an altitude band retain unknown-altitude contacts.
Catalog calls check their cancellation signal immediately before executing a Catalog calls check their cancellation signal immediately before executing a
tool. Already-cancelled queries do not start; cancellation after execution starts tool. Already-cancelled queries do not start; cancellation after execution starts
+2
View File
@@ -111,6 +111,8 @@ export const aircraftInArea = defineTool({
(record) => (record) =>
areaContains(area, { lat: record.latitude, lon: record.longitude }) && areaContains(area, { lat: record.latitude, lon: record.longitude }) &&
!(args.airborne_only && record.onGround) && !(args.airborne_only && record.onGround) &&
((args.min_altitude_m == null && args.max_altitude_m == null) ||
Number.isFinite(altitude(record))) &&
(args.min_altitude_m == null || (args.min_altitude_m == null ||
altitude(record) >= args.min_altitude_m) && altitude(record) >= args.min_altitude_m) &&
(args.max_altitude_m == null || (args.max_altitude_m == null ||
+59
View File
@@ -388,3 +388,62 @@ test('the real OpenSky source marks an hour-old snapshot stale for searches', as
assert.deepEqual(result.data.stale_feeds, ['civil']); assert.deepEqual(result.data.stale_feeds, ['civil']);
assert.match(result.summary, /may be stale\.$/); assert.match(result.summary, /may be stale\.$/);
}); });
test('altitude bands require a reported altitude from normalized OpenSky records', async () => {
const { normalizeOpenSkyAircraft } =
await import('../../sources/live/aircraft.js');
const state = (id, baro, geometric) =>
normalizeOpenSkyAircraft([
id,
'TEST',
'Test',
1767225600,
1767225600,
0,
0,
baro,
false,
10,
90,
0,
[],
geometric,
]);
const records = [
state('abc001', null, null),
state('abc002', 0, null),
state('abc003', null, 50),
state('abc004', 500, null),
];
const catalog = composeCatalog({
tools: coreTools,
services: {
aircraft: { getSnapshot: async () => ({ records }) },
},
});
const area = { bbox: [-1, -1, 1, 1] };
assert.equal(
(await catalog.call('aircraft_in_area', { area })).data.total,
4,
'unfiltered answers retain unknown altitude',
);
for (const band of [
{ max_altitude_m: 100 },
{ min_altitude_m: -500, max_altitude_m: 100 },
]) {
const result = await catalog.call('aircraft_in_area', { area, ...band });
assert.deepEqual(
result.data.rows.map((row) => row.id),
['abc002', 'abc003'],
);
assert.deepEqual(
result.data.rows.map((row) => row.altitude_m),
[0, 50],
);
}
const minimum = await catalog.call('aircraft_in_area', {
area,
min_altitude_m: -500,
});
assert.equal(minimum.data.total, 3);
});