test: prefer SGS hall entrance anchors

This commit is contained in:
lyf
2026-06-30 00:24:08 +08:00
parent c2800bb277
commit e826c92486
2 changed files with 55 additions and 48 deletions

View File

@@ -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.

View File

@@ -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')