Daeseon Yoo
Back to project
·Tech retro·6 min·Review needed

Phase 3.1 verify — adversarial workflow가 BLOCKER 3개 잡았다

Phase 3.1 commit 후 ultracode 정신으로 adversarial verify workflow (5 dimensions × 1 synth). 4개 review가 CRITICAL — verdict FIX_FIRST. SCContentFilter empty array bug + NSScreen.main 자기 모순 + screenFrame.origin 미반영 함정 3개 BLOCKER fix. Sweep plan이 implementation 후 review로만 catch되는 함정 입증.

AI version

Phase 3.1 commit (7f4d4f4) — sweep plan 따라 4 files + AppDelegate edit + dev.sh codesign. swift build + test 통과. 통상이면 commit + 다음 atomic. 단 ultracode 정신은 — substantive task마다 adversarial verify workflow.

띄움. 5 dimensions × 1 synth:

  • ScreenCaptureKit API correctness (Apple docs 인용)
  • 4-layer 좌표 수학 (top-left vs bottom-left)
  • Permissions 흐름 (macOS 14/15 TCC)
  • Multi-monitor edge (hotplug, mixed DPI)
  • Swift 6 concurrency

4분 후 통지 — verdict: FIX_FIRST, ready_for_phase_5: false. CRITICAL 4개.

BLOCKER 3개

1. SCContentFilter empty excludingWindows: [] — documented buffer stall

// Phase 3.1 original
let filter = SCContentFilter(display: display, excludingWindows: [])

Verify agent의 발견 (Federico Terzi 검증 reference 인용):

SCContentFilter(display:excludingWindows:) with empty array — SCStream produces no buffers, capture silently fails. Apple 공식 sample (CapturingScreenContentInMacOS) avoids this initializer.

init(display:excludingApplications:exceptingWindows:) Apple-sanctioned shape:

let filter = SCContentFilter(
    display: display,
    excludingApplications: [],
    exceptingWindows: []
)

동일 의미 (전체 display, 모든 app, 예외 없음) — but buffer stall 안 일어남. solo 작성으로는 vendor SDK bug catch 어려움.

2. NSScreen.main fallback 자기 모순

DisplayGeometry.swift:16:

//  ⚠️ `NSScreen.main` 절대 금지 — 항상 captured display 기준 (Tauri Layer 9 회피).

그런데 ScreenCapture.swift:39 + TriggerContext.swift:32:

?? NSScreen.main  // ← 자기가 박은 rule 위반

Verify agent가 잡음:

'NSScreen.main 절대 금지' 정신 정면 위배. Rare path (cursor가 모든 screen 밖)지만 hard rule.

?? NSScreen.screens.first (macOS process는 최소 1개 screen 보장).

이건 solo 작성에서 매우 잡기 어려운 패턴 — 자기가 박은 rule을 자기 코드 fallback에서 다시 위반. 의식적으로 review하지 않으면 못 봄. adversarial multi-agent가 가치.

3. screenFrame.origin 미반영 → 외부 monitor 함정

logicalRectFromSentBox이 screen-local top-left CGRect 반환. doc comment에 명시:

이 값은 NSScreen 기준 local — 외부 monitor (screenFrame.origin != 0)에선 그대로 NSWindow.setFrame(_:) 호출 X.

단 — helper 없음. Phase 5 HUD code가 직접 산수 (origin add + y flip) 하라는 거. Verify agent:

Phase 5.0 HUDOverlayWindow가 직접 소비할 helper가 빈 상태로 진입하면 Tauri Layer 9 재발 100% 보장.

globalAppKitRect(fromLocalTopLeft:) -> NSRect 추가:

func globalAppKitRect(fromLocalTopLeft local: CGRect) -> NSRect {
    NSRect(
        x: screenFrame.origin.x + local.origin.x,
        y: screenFrame.origin.y + (screenFrame.height - (local.origin.y + local.height)),
        width: local.width,
        height: local.height
    )
}

3 unit tests로 lock:

  1. Primary monitor (origin=0) — y flip만
  2. 외부 monitor x=1440 — origin add + y flip (Tauri Layer 9 직접 시나리오)
  3. Vertical stack origin.y=900 — origin.y 더하기 + y flip

HIGH 3개

4. Permission 거부 후 recovery

Permissions.requestScreenRecording() Bool 반환 — Phase 3.1엔 무시. macOS TCC는 'Don't Allow' 누르면 다이얼로그 영구 재출현 X. 사용자가 거부하면 Settings 안내 없으면 영구 broken.

→ AppDelegate에 1.5s 후 재확인 + Settings URL open:

Permissions.requestScreenRecording()
Task { @MainActor in
    try? await Task.sleep(nanoseconds: 1_500_000_000)
    if !Permissions.hasScreenRecording() {
        Log.app.error("Screen Recording 권한 거부 — Settings > Privacy & Security > Screen Recording에서 ScreenBridge 토글 후 재시작 필요")
        Permissions.openScreenRecordingSettings()
    }
}

5. stale frame/scale

LastTriggerContext가 hotkey 시점 frame/scale 저장. 해상도 변경 후 capture 호출 시 stale → SCStreamConfiguration crop/black bar.

→ displayID만 신뢰 + frame/scale은 capture 시점 NSScreen fresh fetch:

let savedDisplayID = LastTriggerContext.current?.screen.displayID
let screen: NSScreen? = savedDisplayID.flatMap { id in
    NSScreen.screens.first {
        ($0.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber)?.uint32Value == id
    }
}
    ?? NSScreen.screens.first { NSMouseInRect(cursor, $0.frame, false) }
    ?? NSScreen.screens.first
// frame/scale은 지금 fetch — stale 방지
return (id, screen.frame, screen.backingScaleFactor)

6. codesign --identifier 누락

codesign --force -s - 만으론 cdhash가 매 build 변경. TCC 권한 매 build 재요청 → 사용자 좌절 흐름 직역 (memory user-pain-dev-tool-friction).

--identifier com.screenbridge.dev 명시:

codesign --force --sign - --identifier com.screenbridge.dev "$BIN_PATH"

TCC가 identifier + cdhash 조합 기억 → 한 번 권한 → 매 build 유지. 중기 .app bundle로 가면 Info.plist CFBundleIdentifier가 같은 역할.

Sweep workflow의 한계, adversarial verify의 가치

Phase 3.1 시작 전 sweep workflow는 plan을 박았다 — 각 file의 deliverable, sequence, key decisions, risk mitigations. 그러나 코드 작성 후 다 implement된 상태에서만 catch되는 함정들:

  • 벤더 SDK bug (Federico Terzi 검증) — sweep agent가 알 수 없음 (코드 미작성 상태)
  • 자기 모순 fallback — implement 도중 무의식적으로 박음
  • partial conversion helper — sweep plan엔 "DisplayGeometry 4-layer 변환 캡슐화" 명시했지만 helper 완성도는 implement 후 review해야 catch

Sweep (pre-implementation plan) → Implement → Verify (post-implementation adversarial review) — 3단계가 ultracode 정신의 본질. 두 번째 workflow가 비싸 보여도 Tauri 16 layer 디버그 시간 대비 1/100.

Phase 4.2 / 5.x / 6.1 commit 후에도 같은 verify workflow 권장.

4 new tests, 33/33 pass

6 AnalysisResult + 4 Prompts + 8 Env + 6 GeminiDispatcher + 9 DisplayGeometry = 33/33 pass
swift build 2.30s, swift test 0.008s

DisplayGeometry tests 9개로 증가:

  • 기존 5: Retina 다운스케일 / identity / Retina only / invalid box / zero division
  • 신규 4: globalAppKitRect primary / 외부 monitor x=1440 / vertical stack origin.y=900 / 비균일 scaleX/scaleY

다음

Phase 5.0 — HUDOverlayWindow 빈 골격. globalAppKitRect 사용 강제 첫 atomic부터. NSPanel borderless + nonactivating + clear + ignoresMouseEvents=true + level=.screenSaver + collectionBehavior=[.canJoinAllSpaces, .fullScreenAuxiliary, .stationary]. 빨간 박스 1개 hardcode로 NSWindow 5개 본질 (Layer 1/4/7/8/9) 검증 — 사용자 ⌥+Space → 미리 정해진 위치에 빨간 박스 떠보기. Phase 5.0 commit 후에도 adversarial verify workflow.

Review needed

No human review on this entry yet.