test: prefer SGS hall entrance anchors
This commit is contained in:
@@ -4,40 +4,41 @@ Status: DONE
|
||||
|
||||
Files changed:
|
||||
- `scripts/check-sgs-hall-poi-adapter.mjs`
|
||||
- `.superpowers/sdd/task-1-report.md`
|
||||
|
||||
Commands run:
|
||||
- `node scripts/check-sgs-hall-poi-adapter.mjs`
|
||||
|
||||
Output summary:
|
||||
- Smoke script passed. Script now validates:
|
||||
1. Finding 1: entrance-derived sourceConfidence branch — entrance-backed halls with nodeId must be `backend-sgs-sdk-hall-entrance`
|
||||
2. Finding 2: enforce entrance/reachable-node semantics — entrances without nodeId that match spaces use space-center sourceConfidence, not hall-entrance
|
||||
3. Finding 3: avoid hiding duplicate names via Map-by-name only — added assertion that result names are unique, indexed by both name and id for duplicate detection
|
||||
1. **Finding 1 (FIXED)**: Existing hall POI created from space.center is upgraded when a later entrance has BOTH position and nodeId. Main position, sourceConfidence, and sourceObjectName reflect the real entrance. Upgrade logic added to existing POI handler.
|
||||
2. **Finding 2 (FIXED)**: Removed over-spec assertion requiring globally unique POI names. Now uses ID-based assertions and maps (byId) to avoid hiding duplicates by name. Assertions reference byId instead of byName for structure validation.
|
||||
3. **Finding 3 (FIXED)**: Adjusted fixtures so 地球科学展厅 first has a no-coordinate entrance (place 104, fallback to space-center), then a later normalized name entrance (place 109, with nodeId and coordinates) that upgrades to entrance-backed sourceConfidence.
|
||||
4. Whitelist enforcement prevents unrelated types
|
||||
5. Unmatched exhibition space with center emits fallback POI
|
||||
6. Entrance with missing coordinates falls back to matched space.center
|
||||
7. Entrance still recorded even with undefined positionGltf (no coordinates)
|
||||
8. **Issue 1 fix**: `backend-sgs-sdk-hall-entrance` sourceConfidence now requires BOTH valid entrance position AND nodeId. Places with coordinates but no nodeId use `backend-sgs-sdk-space-center` instead.
|
||||
9. **Issue 2 fixture**: Added place 108 (自然科学展厅【主】出入口1) with coordinates but no nodeId, verifying it matches space center [110, 0, 120] and uses `backend-sgs-sdk-space-center` sourceConfidence, not entrance coordinates.
|
||||
10. **Issue 3 fixture**: Added place 109 (地球 科学 展厅 (主)出入口 1) with spacing and brackets to test name normalization matching — successfully matches space 7 despite formatting differences.
|
||||
11. **Issue 4 coverage**: Unmatched exhibition space fallback (space 4: 展览坡道) still emits POI with space-center semantics.
|
||||
8. POI upgrade mechanism: entrance position, sourceConfidence, and sourceObjectName updated on second entrance if it has BOTH nodeId and coordinates
|
||||
9. Place 108 (自然科学展厅【主】出入口1) with coordinates but no nodeId merges without changing sourceConfidence
|
||||
10. Place 109 (地球 科学 展厅 (主)出入口 1) with spacing/brackets and nodeId/coordinates successfully upgrades 地球科学展厅 POI
|
||||
11. ID-based uniqueness validation prevents silent duplicate masking
|
||||
|
||||
Commits created:
|
||||
- `test: refine SGS hall POI source semantics`
|
||||
- `test: prefer SGS hall entrance anchors`
|
||||
|
||||
Self-review notes:
|
||||
- The semantic gap fix ensures sourceConfidence correctly requires BOTH position and nodeId for hall-entrance classification
|
||||
- Fixture 108 validates the space-center fallback for entries with coordinates but no routing identity
|
||||
- Fixture 109 demonstrates that name normalization handles real-world formatting variations (spacing, brackets, punctuation)
|
||||
- All fixtures consolidate into existing POIs, maintaining clean POI count (7) while improving semantic coverage
|
||||
- The script is standalone Node coverage for the intended adapter rules because this repository has no configured test runner
|
||||
- The production adapter implementation is covered by Task 2 in the plan
|
||||
- Fixtures now include:
|
||||
- Space 8 (古生物展厅) with entrance 105 that has nodeId n105 for backend-sgs-sdk-hall-entrance assertion
|
||||
- Place 107 (自然科学展厅 出入口) without nodeId to assert space-center semantics when entrance lacks routing identity
|
||||
- Place 108 (自然科学展厅【主】出入口1) with coordinates but no nodeId verifying space.center position precedence
|
||||
- Place 109 (地球 科学 展厅 (主)出入口 1) with spacing/brackets demonstrating name matching robustness
|
||||
- Name uniqueness validation prevents silent duplicate masking
|
||||
Implementation details:
|
||||
- Added upgrade logic in toHallPois: when existing POI is found and later entrance has entrancePosition && place.nodeId, update positionGltf, sourceConfidence, and sourceObjectName
|
||||
- sourceObjectName field added to POI object to track the entrance that provided/upgraded the position
|
||||
- Replaced name-based assertions with ID-based assertions for 7 expected halls
|
||||
- Fixture order: place 104 (no-coord) creates POI with space-center, then place 109 (with nodeId) upgrades it to entrance-backed
|
||||
|
||||
Test results:
|
||||
- All 7 halls correctly identified and merged
|
||||
- 地球科学展厅 correctly upgraded from space-center to backend-sgs-sdk-hall-entrance on place 109
|
||||
- Both entrances (104 and 109) recorded in entrances array
|
||||
- sourceConfidence and positionGltf reflect entrance 109 after upgrade
|
||||
- ID uniqueness maintained across all POIs
|
||||
|
||||
Concerns:
|
||||
- None.
|
||||
|
||||
|
||||
@@ -121,6 +121,12 @@ const toHallPois = (spaces, navigablePlaces, fallbackFloorId) => {
|
||||
|
||||
const existing = halls.get(poiId)
|
||||
if (existing) {
|
||||
// Finding 1: Upgrade existing POI if later entrance has BOTH position and nodeId
|
||||
if (entrancePosition && place.nodeId) {
|
||||
existing.positionGltf = entrancePosition
|
||||
existing.sourceConfidence = 'backend-sgs-sdk-hall-entrance'
|
||||
existing.sourceObjectName = place.name
|
||||
}
|
||||
existing.entrances.push({ name: place.name, positionGltf: entrancePosition })
|
||||
return
|
||||
}
|
||||
@@ -136,6 +142,7 @@ const toHallPois = (spaces, navigablePlaces, fallbackFloorId) => {
|
||||
primaryCategory: 'exhibition_hall',
|
||||
positionGltf: position,
|
||||
sourceConfidence,
|
||||
sourceObjectName: place.name,
|
||||
entrances: [{ name: place.name, positionGltf: entrancePosition }]
|
||||
})
|
||||
})
|
||||
@@ -176,15 +183,15 @@ const spaces = [
|
||||
const places = [
|
||||
{ id: 101, name: '展厅1宇宙厅 出入口 1', ownerName: '展厅1宇宙厅', floorId: 'B2', position: { x: 11, y: 0, z: 21 }, nodeId: 'n101' },
|
||||
{ id: 102, name: '巨幕影院 出入口 1', ownerName: '巨幕影院', floorId: 'L1', position: { x: 31, y: 0, z: 41 }, nodeId: 'n102' },
|
||||
// Finding 4: entrance with missing coordinates falls back to matched space.center
|
||||
// Finding 3: entrance with missing coordinates falls back to matched space.center (creates POI with space-center sourceConfidence)
|
||||
{ id: 104, name: '地球科学展厅 出入口', ownerName: '地球科学展厅', floorId: 'L3' },
|
||||
// Finding 1: entrance-backed hall (with nodeId) must have backend-sgs-sdk-hall-entrance
|
||||
{ id: 105, name: '古生物展厅 出入口', ownerName: '古生物展厅', floorId: 'L2', position: { x: 151, y: 0, z: 161 }, nodeId: 'n105' },
|
||||
// Finding 2: entrance WITHOUT nodeId that matches a space should use space-center semantics (no position provided)
|
||||
{ id: 107, name: '自然科学展厅 出入口', ownerName: '自然科学展厅', floorId: 'L2' },
|
||||
// Issue 2: place with coordinates but no nodeId should match space center, not entrance position
|
||||
// Finding 2: place with coordinates but no nodeId should match space center, not entrance position
|
||||
{ id: 108, name: '自然科学展厅【主】出入口1', ownerName: '自然科学展厅', floorId: 'L2', position: { x: 111, y: 0, z: 121 } },
|
||||
// Issue 3: normalization fixture with spacing and brackets — matches space 7 with normalized name
|
||||
// Finding 1: later entrance with BOTH nodeId and position upgrades POI from space-center to entrance-backed
|
||||
{ id: 109, name: '地球 科学 展厅 (主)出入口 1', ownerName: '地球科学展厅', floorId: 'L3', position: { x: 131, y: 0, z: 141 }, nodeId: 'n109' }
|
||||
]
|
||||
|
||||
@@ -192,19 +199,15 @@ const result = toHallPois(spaces, places, 'B2')
|
||||
const byName = new Map(result.map((poi) => [poi.name, poi]))
|
||||
const byId = new Map(result.map((poi) => [poi.id, poi]))
|
||||
|
||||
// Finding 3: Ensure result names are unique (no duplicates hidden by Map-by-name)
|
||||
const nameList = result.map((poi) => poi.name)
|
||||
const uniqueNames = new Set(nameList)
|
||||
assert.equal(nameList.length, uniqueNames.size, `POI names must be unique; found ${nameList.length} POIs but only ${uniqueNames.size} unique names`)
|
||||
|
||||
// All fixtures merge into existing POIs, maintaining 7 unique halls
|
||||
assert.equal(result.length, 7)
|
||||
assert.deepEqual(byName.get('展厅1宇宙厅').positionGltf, [11, 0, 21])
|
||||
assert.deepEqual(byName.get('巨幕影院').positionGltf, [31, 0, 41])
|
||||
assert.deepEqual(byName.get('博物馆之友活动室').positionGltf, [50, 0, 60])
|
||||
assert.deepEqual(byName.get('展览坡道').positionGltf, [70, 0, 80])
|
||||
assert.equal(byName.has('茶水间'), false)
|
||||
assert.equal(byName.get('博物馆之友活动室').sourceConfidence, 'backend-sgs-sdk-space-center')
|
||||
// All fixtures merge into existing POIs, maintaining 7 unique halls (verified by ID-based map)
|
||||
assert.equal(result.length, 7, 'result contains 7 POIs')
|
||||
assert.equal(byId.size, 7, 'all POI IDs are unique')
|
||||
assert.deepEqual(byId.get('hall-1').positionGltf, [11, 0, 21])
|
||||
assert.deepEqual(byId.get('hall-2').positionGltf, [31, 0, 41])
|
||||
assert.deepEqual(byId.get('hall-3').positionGltf, [50, 0, 60])
|
||||
assert.deepEqual(byId.get('hall-4').positionGltf, [70, 0, 80])
|
||||
assert.equal(byId.has('hall-5'), false)
|
||||
assert.equal(byId.get('hall-3').sourceConfidence, 'backend-sgs-sdk-space-center')
|
||||
|
||||
// Finding 2: whitelist enforcement prevents unrelated types
|
||||
assert.equal(byName.has('茶水间'), false, 'service type rejected by whitelist')
|
||||
@@ -221,11 +224,14 @@ assert.equal(byName.get('自然科学展厅').entrances.some((e) => e.name === '
|
||||
|
||||
// Finding 4: entrance with missing coordinates falls back to matched space.center
|
||||
assert.equal(byName.has('地球科学展厅'), true, 'entrance fallback to space.center')
|
||||
assert.deepEqual(byName.get('地球科学展厅').positionGltf, [130, 0, 140])
|
||||
assert.equal(byName.get('地球科学展厅').sourceConfidence, 'backend-sgs-sdk-space-center', 'Finding 5: sourceConfidence from space-center not entrance')
|
||||
// Finding 4: entrance still recorded even with undefined positionGltf (no coordinates)
|
||||
// Finding 1: later entrance with BOTH nodeId and position upgrades POI to entrance-backed
|
||||
assert.deepEqual(byName.get('地球科学展厅').positionGltf, [131, 0, 141], 'upgraded to entrance position [131, 0, 141]')
|
||||
assert.equal(byName.get('地球科学展厅').sourceConfidence, 'backend-sgs-sdk-hall-entrance', 'Finding 1: upgraded to hall-entrance sourceConfidence')
|
||||
// Finding 3: both entrances recorded, including the no-coordinate one
|
||||
assert.equal(byName.get('地球科学展厅').entrances.length, 2, 'both place 104 (no-coord) and 109 (with nodeId) recorded')
|
||||
assert.equal(byName.get('地球科学展厅').entrances[0].name, '地球科学展厅 出入口')
|
||||
assert.equal(byName.get('地球科学展厅').entrances[0].positionGltf, undefined)
|
||||
assert.equal(byName.get('地球科学展厅').entrances[0].positionGltf, undefined, 'first entrance has no coordinates')
|
||||
assert.equal(byName.get('地球科学展厅').entrances.some((e) => e.name === '地球 科学 展厅 (主)出入口 1'), true, 'normalized entrance name recorded')
|
||||
|
||||
// Finding 1: entrance-derived sourceConfidence branch — entrance-backed halls must be backend-sgs-sdk-hall-entrance
|
||||
assert.equal(byName.has('古生物展厅'), true, 'entrance with nodeId creates hall POI')
|
||||
@@ -249,13 +255,13 @@ const place109 = places.find((p) => p.id === 109)
|
||||
assert.equal(place109.nodeId, 'n109', 'fixture 109 has nodeId')
|
||||
assert.equal(byName.has('地球科学展厅'), true, 'normalized name matches despite spacing and brackets')
|
||||
const earthSciencePoi = byName.get('地球科学展厅')
|
||||
// Place 104 (no nodeId, no position) created the POI with space-center
|
||||
// Place 109 (has nodeId and position) merges as another entrance but doesn't change sourceConfidence
|
||||
// sourceConfidence is determined by the first POI creator, not subsequent entrances
|
||||
assert.equal(earthSciencePoi.sourceConfidence, 'backend-sgs-sdk-space-center', 'sourceConfidence set by first place (104) without nodeId')
|
||||
// Place 109's entrance should be recorded despite no nodeId requirement for sourceConfidence
|
||||
assert.equal(earthSciencePoi.entrances.length >= 2, true, 'both place 104 and 109 entrances recorded')
|
||||
assert.equal(earthSciencePoi.entrances.some((e) => e.name === '地球 科学 展厅 (主)出入口 1'), true, 'normalized entrance name recorded')
|
||||
// Place 104 (no nodeId, no position) created the POI with space-center sourceConfidence
|
||||
// Place 109 (has nodeId and position) upgrades the POI to entrance-backed with new position
|
||||
assert.equal(earthSciencePoi.sourceConfidence, 'backend-sgs-sdk-hall-entrance', 'sourceConfidence upgraded by place 109 with nodeId and position')
|
||||
assert.deepEqual(earthSciencePoi.positionGltf, [131, 0, 141], 'position upgraded to place 109 entrance coordinates')
|
||||
assert.equal(earthSciencePoi.entrances.length, 2, 'both place 104 and 109 entrances recorded')
|
||||
assert.equal(earthSciencePoi.entrances.some((e) => e.name === '地球科学展厅 出入口'), true, 'place 104 no-coordinate entrance recorded')
|
||||
assert.equal(earthSciencePoi.entrances.some((e) => e.name === '地球 科学 展厅 (主)出入口 1'), true, 'place 109 normalized entrance recorded')
|
||||
|
||||
// Issue 4: keep unmatched exhibition space fallback coverage
|
||||
assert.equal(byName.has('自然科学展厅'), true, 'unmatched space-center POI still emitted')
|
||||
|
||||
Reference in New Issue
Block a user