/* * ★ 현장으로 물으면 멈춘 트윈의 기록도 나온다 — 저널은 기록이고, 지금 도는지는 다른 축이다. * * ── 무엇이 났나 (2026-08-28 실측) ─────────────────────────────────────────── * 대상 해소에 `running` 조건이 있었다. 그래서 현장으로 물으면 멈춘 트윈이 대상에서 빠지고, 화면은 * 「기록이 없습니다」라고 말했다 — 저널에는 있는데. * * twinEventList(instanceId: "ems-plant-1") 1,792건 * twinEventList(spaceId: "ems-plant-1-site") 0건 ← 같은 트윈 · 같은 도메인 * * 규모로 보면 예외가 아니라 기본값이었다: 등록된 트윈 30개 중 도는 것 2개, 멈춘 것 28개. * * ── 왜 지웠나 ────────────────────────────────────────────────────────────── * 옛 주석이 대는 이유(「벤치 사본은 사용자가 보는 현장이 아니다」)는 벤치 제외만 설명한다. `running` 에는 * 근거가 적혀 있지 않았다. 그리고 바로 다음 줄이 반대 원칙을 적어 두었다 — 「정지·비등록이어도 저널은 * 남아 있을 수 있다」. 그 원칙이 현장 경로에만 적용되지 않고 있었다. */ import { test } from 'node:test' import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import { join } from 'node:path' const KPI = join(import.meta.dirname, '..', 'server', 'engine', 'kpi-query.ts') const src = readFileSync(KPI, 'utf8') /** 대상을 고르는 그 한 줄만 떼어 본다. */ const targetLine = (() => { const from = src.indexOf('async function resolveTargets') const body = src.slice(from, from + 1200) const m = /: list\.filter\(\(x: any\) => [^\n]+\)/.exec(body) return m?.[0] ?? '' })() test('★ 현장 경로가 「지금 도는가」로 걸러내지 않는다', () => { assert.ok(targetLine, '대상을 고르는 줄을 찾아야 한다') assert.doesNotMatch(targetLine, /running/, '멈춘 트윈을 현장에서 빼면 화면이 거짓 「없다」를 낸다') }) test('벤치 사본은 계속 뺀다 — 그 제외에는 근거가 있다', () => { /* 부하 시험용 사본은 사용자가 보는 현장이 아니다. 이 제외는 지우지 않는다. */ assert.match(targetLine, /purpose !== 'bench'/) }) test('현장과 트윈 두 경로가 같은 원칙을 쓴다 — 저널은 기록이다', () => { /* 트윈을 지정한 경로는 처음부터 「정지·비등록이어도 저널은 남아 있을 수 있다」였다. */ assert.match(src, /정지·비등록\)? ?지정된 트윈은 그대로 조회한다|정지·비등록/) /* 왜 지웠는지가 코드에 남아 있어야 한다 — 다음 사람이 성능을 이유로 되넣지 않게. */ assert.match(src, /「지금 도는가」로 걸러내지 않는다/) assert.match(src, /1,792건/, '무엇을 재서 알았는지 적혀 있어야 한다') }) test('★ 「지금 도는 것만」이 필요하면 이름 있는 축으로 받는다 — 여기 숨기지 않는다', () => { /* 한 칸에 두 물음을 담으면 어느 화면이 무엇을 보는지 아무도 모른다. */ assert.match(src, /\*\*이름 있는 축\*\*으로 받는다/) })