Bug List
Status key: ✅ Fixed | 🔴 Critical | 🟡 Medium | 🔵 Low
Round 20 — 2026-04-22 (Card Feature Focused Audit — pre-submission)
| ID | File | Description | Priority | Status |
|---|---|---|---|---|
| R20-A01 | CardManager.swift |
saveDraft() persists editToken in plaintext UserDefaults — secret exposure in backups |
重大 | ✅ Fixed 2026-04-22 (strip editToken before encoding draft) |
| R20-A02 | CardKeychain.swift |
save() overwrites stored token with empty string when resolveEditToken transiently returns "" — silent token loss |
中 | ✅ Fixed 2026-04-22 (skip set(tokenAccount) if editToken is empty) |
| R20-A03 | CardModel.swift / CardWriteView.swift |
publicURL returns https://.nfc.bz when username empty; NFC write proceeds with invalid URL |
中 | ✅ Fixed 2026-04-22 (empty-username guard in computed property + CardWriteView.writeToTag) |
| R20-B01 | CardEditorView.swift / CardUsernamePickerView.swift / Localizable.xcstrings |
TextField placeholder "username" hardcoded English — no Japanese localization |
中 | ✅ Fixed 2026-04-22 (added card.username.placeholder key, en + ja) |
Round 19 — 2026-04-22 (5-Agent: NFC/Auth/CardUI/StoreKit/History)
| ID | File | Description | Priority | Status |
|---|---|---|---|---|
| R19-A01 | NFCWriter.swift |
writeToTag callback and didInvalidateWithError use implicit self without [weak self] — use-after-free if session outlives writer |
重大 | ✅ Fixed 2026-04-22 ([weak self] + guard in both closures) |
| R19-B01 | HistoryView.swift |
.onDelete closure reads visibleItems inline — TOCTOU race with iCloud merge can delete wrong items |
重大 | ✅ Fixed 2026-04-22 (snapshot visibleItems at swipe time) |
| R19-C01 | AnalysisView.swift |
RecordFixSheet write success not recorded to history — writes via NFC fix are invisible in history |
重大 | ✅ Fixed 2026-04-22 (added @EnvironmentObject var history, call history.add(url: finalValue) on success) |
| R19-D01 | WriteView.swift |
History records current urlText, not the URL actually written — user edits between tap and success corrupt history |
中 | ✅ Fixed 2026-04-22 (lastWrittenURL snapshot before nfcWriter.write()) |
Round 18 — 2026-04-22 (5-Agent Deep Audit: NFC/StoreKit/TeamMode/ToolForm/Widget)
| ID | File | Line(s) | Description | Priority | Status | ||
|---|---|---|---|---|---|---|---|
| R18-A01 | NFCReader.swift |
~38 | Double-session possible: read() creates new session without invalidating prior — violates CoreNFC single-session contract |
中 | ✅ Fixed 2026-04-22 (session?.invalidate(); session = nil before new session) |
||
| R18-B01 | ToolFormHelpers.swift |
~451 | geocodeAddress() creates new CLGeocoder per call — stale results from earlier calls can overwrite current state |
中 | ✅ Fixed 2026-04-22 (stale-result guard: check input unchanged before applying result) | ||
| R18-C01 | ToolFormView.swift |
~683 | performReviewWrite() missing guard !isCreatingTag — double-tap spends two review credits before NFC starts |
重大 | ✅ Fixed 2026-04-22 (added guard !isCreatingTag else { return } at function entry) |
||
| R18-C02 | ToolFormView.swift |
~442 | Dead `onChange(of: isWriting | isShortening)` with empty body — unnecessary SwiftUI reactivity overhead | 低 | ✅ Fixed 2026-04-22 (removed empty onChange) | |
| R18-D01 | TeamManager.swift |
~65 | syncWithServer() missing concurrency guard — rapid foreground entries spawn concurrent syncs mutating members |
中 | ✅ Fixed 2026-04-22 (syncInFlight guard added) |
||
| R18-D02 | SettingsView.swift |
~534,259 | Hardcoded Japanese strings "サインインして機能を有効化" and "管理ダッシュボード" — not localized, breaks i18n |
重大 | ✅ Fixed 2026-04-22 (added settings.account.signInPromptTitle, settings.account.signInPromptSubtitle, settings.admin.dashboard to Localizable.xcstrings) |
Round 17 — 2026-04-22 (5-Agent Deep Audit: Concurrency/UX/Architecture/Edge Cases/Integration)
| ID | File | Description | Priority | Status |
|---|---|---|---|---|
| R17-A21 | WriteEventUploader.swift |
flushQueue drains array before upload → events lost on failure |
重大 | ✅ Fixed 2026-04-22 (snapshot + failedIDs set; only successful uploads removed) |
| R17-A22 | NFCWriter.swift |
verifyAndFinish Task does not check isCancelled after sleep → writes to invalidated session |
中 | ✅ Fixed 2026-04-22 (guard !Task.isCancelled after sleep) |
| R17-A23 | AuthManager.swift |
Keychain.setData silently swallows SecItemAdd failures in production |
軽微 | ✅ Fixed 2026-04-22 (#if DEBUG print for SecItemAdd failure) |
| R17-A24 | CardManager.swift |
update() has no concurrent-call guard — second call with stale snapshot can overwrite first |
中 | ✅ Fixed 2026-04-22 (guard !isBusy added) |
| R17-B15 | CardEditorView.swift |
Kana auto-fill flag lastNameKanaUserEdited never reset when kanji field cleared → kana locked permanently |
中 | ✅ Fixed 2026-04-22 (onChange on kanji field resets flag when empty) |
| R17-B17 | ToolFormComponents.swift |
PhoneFormField.onAppear { showKeyboard = true } re-triggers keyboard on every tab switch |
中 | ✅ Fixed 2026-04-22 (removed .onAppear keyboard trigger) |
| R17-B19 | CardPreviewView.swift |
ForEach(contacts, id: \.label) crashes on duplicate labels (e.g. two websites) |
中 | ✅ Fixed 2026-04-22 (ForEach(Array(enumerated()), id: \.offset)) |
| R17-B21 | SettingsView.swift |
Sign-in prompt fires on every tab switch to Settings instead of once per session | 中 | ✅ Fixed 2026-04-22 (hasShownSignInPrompt state flag) |
| R17-C13 | TeamManager.swift |
removeMember missing isBusy = true / defer isBusy = false → double-tap deletes member twice |
中 | ✅ Fixed 2026-04-22 (added isBusy guard + defer) |
| R17-C15 | UserDefaultsKeys.swift / NFCTagLabelSync.swift |
labelSyncLastRun key hardcoded in NFCTagLabelSync — not in UDKey enum |
軽微 | ✅ Fixed 2026-04-22 (added UDKey.labelSyncLastRun, updated sync file) |
| R17-C21 | TeamManager.swift |
addMember calls server before checking member limit — wastes credits on over-limit attempt |
中 | ✅ Fixed 2026-04-22 (pre-flight limit check via memberLimit parameter) |
| R17-D14 | CardManager.swift |
refreshCard returns nil (card deleted server-side) but local state not cleared → ghost card UI |
中 | ✅ Fixed 2026-04-22 (nil → clear all local layers, show NoCardView) |
| R17-D16 | OCRScannerView.swift |
didTapOn emits empty transcript for some text regions → wipes bound field |
中 | ✅ Fixed 2026-04-22 (guard !recognized.isEmpty) |
| R17-D18 | TeamManager.swift |
syncWithServer catches .notFound with generic handler — ghost team UI persists when team deleted |
中 | ✅ Fixed 2026-04-22 (catch CardClientError.notFound → reset()) |
| R17-D21 | StoreManager.swift |
isTrialActive evaluated only in init() — expires mid-session but UI still shows Pro features |
中 | ✅ Fixed 2026-04-22 (re-evaluated in refreshEntitlements()) |
| R17-D22 | NFCCopier.swift |
Empty source tag: state = .failed set asynchronously after session.invalidate() — UI shows wrong state |
軽微 | ✅ Fixed 2026-04-22 (set state synchronously before invalidate) |
Round 16 — 2026-04-22 (5-Agent Deep Audit: Runtime/UX/Design/QA/Integration)
| ID | File | Description | Priority | Status |
|---|---|---|---|---|
| R16-A16 | NFCWriter.swift |
isClearOperation race: read/reset inside CoreNFC callback after concurrent didInvalidateWithError reset |
中 | ✅ Fixed 2026-04-22 (capture to let wasClear before sleep) |
| R16-A17 | NFCReader.swift |
parseRecord wraps @Published writes in DispatchQueue.main.async unnecessarily — ordering fragility |
軽微 | 🔵 Tracked |
| R16-A18 | StoreManager.swift |
syncTransactionToNfcBz failure sets lastErrorMessage — shows error toast after successful purchase |
中 | ✅ Fixed 2026-04-22 (demoted to debug log only) |
| R16-A19 | CardModeRootView.swift |
editTokenTask.cancel() only on inner view's onDisappear — leaks Task on mode switch |
軽微 | 🔵 Tracked |
| R16-A20 | CardModeRootView.swift |
saving = true set twice in create() — redundant write, maintenance trap |
軽微 | ✅ Fixed 2026-04-22 (removed from inside create()) |
| R16-B01 | Various | ResultBanner never auto-dismisses — known, tracked in issues.md UX-02 |
軽微 | 🔵 Known (tracked) |
| R16-B02 | HistoryView.swift |
History rewrite button: guard against concurrent rewrite already covered by isWriting check in row |
中 | 🔵 Tracked |
| R16-B03 | CardEditorView.swift |
LabeledTextField label 100pt fixed width truncates long localized strings |
中 | 🔵 Tracked (todo.md) |
| R16-B05 | SimpleWriteView.swift |
History rewrite button has no .disabled() during active NFC session |
中 | ✅ Fixed 2026-04-22 (added .disabled + gray background) |
| R16-B07 | PaywallView.swift |
Empty products (StoreKit offline) shows ProgressView() forever — no error state |
中 | ✅ Fixed 2026-04-22 (shows error icon when not loading and empty) |
| R16-B09 | CardModeRootView.swift |
Username TextField .fixedSize(horizontal: true) overflows on long input |
中 | 🔵 Tracked (todo.md) |
| R16-B10 | HistoryView.swift |
Trash button visible even when only tap history exists (write list empty) | 軽微 | ✅ Fixed 2026-04-22 (guarded by !visibleItems.isEmpty) |
| R16-B11 | SimpleWriteView.swift |
TextFields lack .submitLabel / .onSubmit — Return key does nothing |
中 | 🔵 Tracked (todo.md) |
| R16-B13 | PaywallView.swift |
CreditPackCard selection border: same color as background → invisible |
中 | ✅ Fixed 2026-04-22 (white border when selected) |
| R16-C01 | ToolFormView.swift |
shortcutHistoryKey instance let instead of static let |
中 | ✅ Fixed 2026-04-22 (static let + Self. prefix) |
| R16-C02 | ToolFormView.swift / UserDefaultsKeys.swift |
reviewHistoryKey / shortcutHistoryKey / toolFormStorage not in UDKey enum |
中 | ✅ Fixed 2026-04-22 (added to UDKey) |
| R16-C04 | TeamClient.swift / SupabaseCardClient.swift |
encodeCard duplicated 60+ lines — field drift causes silent data loss in team cards |
中 | ✅ Fixed 2026-04-22 (extracted MyCard.encodeForAPI(), removed duplicates) |
| R16-C05 | SupabaseCardClient.swift |
fetchEditToken hardcodes Supabase project URL — breaks on project migration |
中 | ✅ Fixed 2026-04-22 (uses SupabaseClient.url + path) |
| R16-C08 | ToolFormHelpers.swift |
_ = item suppress warning masks readability — item IS used, but misleading |
軽微 | ✅ Fixed 2026-04-22 (changed var to let, removed _ = item) |
| R16-D01 | CardManager.swift |
suggestUsername(maxAttempts:) crashes with fatal range error when maxAttempts < 2 |
中 | ✅ Fixed 2026-04-22 (max(2, maxAttempts) guard) |
| R16-D02 | WriteEventUploader.swift |
Concurrent enqueue Task + flushQueue can produce duplicate uploads |
中 | 🔵 Tracked (server-side client_event_id dedup mitigates) |
| R16-D03 | WriteView.swift / SearchWriteView.swift |
No scenePhase handler — isWriting stuck true when app backgrounds mid-NFC |
中 | ✅ Fixed 2026-04-22 (added onChange(of: scenePhase) matching ToolFormView pattern) |
| R16-D12 | CardUsernamePickerView.swift |
Offline: availability check returns nil → .idle state — user cannot proceed with valid username |
中 | ✅ Fixed 2026-04-22 (.networkError state allows proceed; server validates on insert with 409) |
Round 15 — 2026-04-22 (5-Agent Deep Audit: NFC/Async/Session/Memory)
| ID | File | Description | Priority | Status |
|---|---|---|---|---|
| R15-01 | NFCAnalyzer.swift, NFCWriter.swift |
Strong self capture in NFC delegate closures → retain cycle between session and analyzer |
中 | ✅ Fixed 2026-04-22 ([weak self] + guard) |
| R15-02 | NFCWriter.swift |
verifyAndFinish dispatches to DispatchQueue.global() — @Published reads from background thread |
中 | ✅ Fixed 2026-04-22 (Task { [weak self] } + sleep) |
| R15-03 | CardModeRootView.swift |
checkTask not cancelled on view disappear → availability check runs after dismiss |
中 | ✅ Fixed 2026-04-22 (.onDisappear { checkTask?.cancel() }) |
| R15-04 | TeamAddMemberView.swift |
Username collision not server-checked, no recovery UX when taken | 中 | 🔵 Tracked (todo.md — needs API contract change) |
| R15-05 | TeamMemberDetailView.swift |
openEditInBrowser proceeds with empty editToken — opens auth-less editor URL |
中 | ✅ Fixed 2026-04-22 (guard empty token) |
| R15-06 | CardEditorView.swift |
OneShotLocationDelegate.keepAlive static var — concurrent instances corrupt each other |
軽微 | 🔵 Accepted (single-instance in practice) |
| R15-07 | TeamClient.swift, SupabaseCardClient.swift |
Card encoding logic duplicated — divergence risk | 軽微 | 🔵 Tracked (refactor candidate) |
| R15-08 | NFCCopier.swift |
startWritingDestination overwrites session without invalidating prior — two concurrent sessions possible |
中 | ✅ Fixed 2026-04-22 (invalidate+nil before new session) |
| R15-09 | SimpleWriteView.swift |
idHistory.record uses live username not actually-written ID on quick-rewrite |
軽微 | 🔵 Accepted (narrow race, no user reports) |
| R15-10 | StoreManager.swift |
Pro purchase grants 3 credits without dedup — double-grant possible on sandbox restore | 中 | ✅ Fixed 2026-04-22 (dedup via grantedTxIDs with pro_bonus_ prefix) |
| R15-11 | PaywallView.swift |
Auto-dismiss only on isPro, not isLiveTagSubscriber/isTeam |
軽微 | ✅ Fixed 2026-04-22 (added onChange for both) |
| R15-12 | HistoryManager.swift |
DateFormatter re-created on every save() call — allocation waste |
軽微 | ✅ Fixed 2026-04-22 (lazy var widgetDateFormatter) |
| R15-13 | TeamManager.swift |
Raw string literals for UserDefaults keys, not in UDKey enum |
軽微 | 🔵 Tracked (refactor candidate) |
| R15-14 | NFCWriter.swift |
cancelSession() does not reset sessionAlertMessage → wrong prompt on next session |
軽微 | ✅ Fixed 2026-04-22 (reset to default in cancelSession) |
| R15-15 | FriendCardView.swift |
Menu write items bypass .disabled() — second NFC session possible from Menu |
中 | ✅ Fixed 2026-04-22 (guard in writeSingle()) |
| R15-16 | AuthManager.swift |
5s timeout cancelAll() doesn't stop URLSession — signed-in state arrives after isRestoring=false |
中 | 🔵 Accepted (benign UI jump; URLSession cancellation complex) |
| R15-17 | SearchWriteView.swift |
runSearch Task unretained — cannot cancel on dismiss, stale results applied |
中 | ✅ Fixed 2026-04-22 (@State searchTask + cancel on disappear) |
| R15-18 | CardManager.swift |
KVS observer token not stored — removeObserver never called, dangling entry accumulates |
中 | ✅ Fixed 2026-04-22 (kvsObserver property + deinit) |
Round 14 — 2026-04-22 (5-Agent Deep Audit: Security/Runtime/UX/Maintainability)
| ID | File | Description | Priority | Status |
|---|---|---|---|---|
| R14-01 | SupabaseClient.swift |
Supabase anon key hardcoded in source (SupabaseCardClient already uses Info.plist) | 重大 | 🔵 Accepted (anon key is public by design; tracked in issues.md) |
| R14-02 | ContentView.swift |
Admin email check duplicated inline instead of using auth.isAdmin — divergence risk |
🟡 | ✅ Fixed 2026-04-22 |
| R14-03 | ToolFormView.swift |
Dead code if false && ... preview block never executes |
🔵 | ✅ Fixed 2026-04-22 (removed) |
| R14-04 | NFCWriter/NFCCopier/NFCAnalyzer |
GCD + Swift concurrency mixed (DispatchQueue.main.async on @Published) |
🟡 | 🔵 Accepted (functional today; tracked for future strict-concurrency migration) |
| R14-05 | CardEditorView.swift |
OneShotLocationDelegate.keepAlive static — concurrent instances can orphan continuation |
🟡 | 🔵 Accepted (not reproducible in current nav structure; same as R12-10) |
| R14-06 | LocationManager.swift |
requestOnce can permanently latch isRequesting=true on .denied race |
🟡 | 🔵 Accepted (.denied path clears correctly; edge case not reproducible) |
| R14-07 | HistoryManager.swift |
mergeItems calls save() during init() chain — double-encode on cold launch |
🟡 | 🔵 Accepted (startup perf acceptable; tracked for future refactor) |
| R14-08 | CardManager.swift |
syncWithServer has no timeout cancellation beyond URLSession 15s |
🟡 | 🔵 Accepted (guard flag prevents stack; URLSession timeout sufficient) |
| R14-09 | CardModeRootView.swift |
CreateCardFlow.create() double-tap: saving=true set inside async body, not before Task |
🟡 | ✅ Fixed 2026-04-22 |
| R14-10 | CardModeRootView.swift |
openEditInBrowser edit token visible in Safari history |
🟡 | 🔵 Accepted (architectural; server-side token expiry is mitigation) |
| R14-11 | StoreManager.swift |
observeTransactionUpdates credit: finish() called AFTER addCredits in purchase path but BEFORE in updates path |
🟡 | ✅ Fixed 2026-04-22 (finish now always after addCredits) |
| R14-12 | SettingsView.swift |
runLabelSync debug string with auth state/token shown in production builds |
🟡 | ✅ Fixed 2026-04-22 (#if DEBUG guard) |
| R14-13 | AnalysisView.swift |
onAppear auto-triggers NFC scan without user gesture — UX anti-pattern |
🟡 | ✅ Fixed 2026-04-22 (removed auto-trigger) |
| R14-14 | SimpleWriteView.swift |
Quick-rewrite history entry diverges from URL record on rapid input | 🔵 | 🔵 Accepted (narrow edge case; no user reports) |
| R14-15 | ToolFormView.swift |
791-line View mixing GPS/NFC/StoreKit/network — maintainability debt | 🟡 | 🔵 Tracked (todo.md) |
| R14-16 | WriteView.swift |
Non-Japan write path does not record history | 🟡 | 🔵 Tracked (requires history API change) |
| R14-17 | NFCCopier.swift |
No verify-after-write in copy session B (NFCWriter has verify; Copier does not) | 🟡 | 🔵 Tracked (todo.md) |
| R14-18 | AuthManager.swift |
restoreSession timeout: group.cancelAll() mid-apply() can leave partial state |
🔵 | 🔵 Accepted (5s timeout generous; apply() partial state benign) |
| R14-19 | SettingsView.swift |
UserDefaults.standard.synchronize() called — deprecated API |
🔵 | ✅ Fixed 2026-04-22 (removed; iCloud KVS .synchronize() kept) |
| R14-20 | ToolFormView.swift |
.foregroundStyle(store.reviewCredits > 0 ? .white : .white) — dead ternary |
🔵 | ✅ Fixed 2026-04-22 (simplified to .white) |
| R14-21 | CardEditorView.swift |
showToast("card.toast.locationDenied") passes raw String not LocalizedStringKey |
🔵 | 🔵 Accepted (works via LocalizedStringKey(string:) init; visual risk low) |
| R14-22 | WriteEventUploader.swift |
enqueue fires upload then queues on failure — process kill between upload-start and appendToQueue loses event |
🟡 | ✅ Fixed 2026-04-22 (queue first, upload second, remove on success) |
| R14-23 | AnalysisView.swift |
navigationTitle("sns.navigationTitle") wrong key |
🔵 | ✅ Fixed 2026-04-22 ("analysis.navigationTitle") |
| R14-24 | HistoryView.swift |
visibleItems returns all items but section header says "writes" |
🟡 | ✅ Fixed 2026-04-22 (filter to .write type) |
| R14-25 | HistoryView.swift |
Quick-rewrite creates duplicate history entry with same short URL | 🔵 | 🔵 Accepted (by design; UX noted in issues.md) |
| R14-26 | StoreManager.swift |
Credit dedup set trimming uses Set→Array.suffix which is non-deterministic order |
🟡 | ✅ Fixed 2026-04-22 (ordered Array with append+suffix) |
Round 13 — 2026-04-22 (5-Agent Full Audit: CRITICAL/HIGH priority)
| ID | Repo | Description | Priority | Status |
|---|---|---|---|---|
| R13-01 | NFCTool | CardManager.init fixup: reinstall clears Keychain even when valid username exists → card lost on reinstall |
重大 | ✅ Fixed 2026-04-22 |
| R13-02 | NFCTool | CardManager.create: Keychain not pre-written before insert() → kill between insert-complete and persistAll causes permanent card loss |
重大 | ✅ Fixed 2026-04-22 |
| R13-03 | NFCTool | CardEditorView: OneShotAuthDelegate missing forceResolve() + inner continuation has no withTaskCancellationHandler → continuation leak on Task cancel |
重大 | ✅ Fixed 2026-04-22 |
| R13-04 | NFCTool | CardManager.syncWithServer: no in-flight guard → .task + willEnterForeground run concurrently → double card write |
HIGH | ✅ Fixed 2026-04-22 |
| R13-05 | NFCTool | CardManager: editToken 3-tier fallback copy-pasted in refreshCard + restoreFromServer → maintenance trap |
HIGH | ✅ Fixed 2026-04-22 (resolveEditToken shared method) |
| R13-06 | NFCTool | openEditInBrowser: Task.isCancelled check only after fetchEditToken completes → dismissed view can still open Safari |
HIGH | ✅ Fixed 2026-04-22 |
| R13-07 | NFCTool | checkUsernameAvailable catch returns true (available) on network error → offline user can attempt create and get 409 |
HIGH | ✅ Fixed 2026-04-22 (Bool? tristate + "offline" UI) |
| R13-08 | NFCTool | editTokenErrorMessage never auto-clears → red error banner stays forever until tapped |
中 | ✅ Fixed 2026-04-22 (4s auto-dismiss) |
| R13-09 | NFCTool | CardPreviewView.collectSocials missing Threads → Threads SNS not shown in card preview (Widget shows it) |
中 | ✅ Fixed 2026-04-22 |
| R13-10 | NFCTool | KVS didChangeExternally: only calls loadFromLocal, not syncWithServer → multi-device edits not reflected |
中 | ✅ Fixed 2026-04-22 |
| R13-11 | NFCTool | SecondaryButton struct: dead code (defined but never used) |
軽微 | ✅ Fixed 2026-04-22 (deleted) |
Round 12 — 2026-04-22 (Multi-Agent Deep Audit: CardMode/NFC/StoreKit/QR)
| ID | Repo | Description | Priority | Status |
|---|---|---|---|---|
| R12-01 | NFCTool | StoreManager.purchase credits: finish() called before dedup ID persisted — kill window causes silent credit loss |
重大 | ✅ Fixed 2026-04-22 |
| R12-02 | NFCTool | openEditInBrowser silently does nothing when editToken empty after all fallbacks (no toast in Release) |
重大 | ✅ Fixed 2026-04-22 |
| R12-03 | NFCTool | CardEditorView.fetchCurrentAddress: requestLocation() called before authorization dialog answered — always fails on first use |
重大 | ✅ Fixed 2026-04-22 |
| R12-04 | NFCTool | CreateCardFlow checking spinner hangs forever after rapid delete below 3 chars (cancel exits without checking = false) |
中 | ✅ Fixed 2026-04-22 |
| R12-05 | NFCTool | QRScannerSheet.updateUIViewController calls startScanning() on every SwiftUI re-render; QR requires manual tap (no didAdd) |
中 | ✅ Fixed 2026-04-22 |
| R12-06 | NFCTool | CardWriteView: no onDisappear { cancelSession() } → isWriting stuck true; no auto-dismiss after write success |
中 | ✅ Fixed 2026-04-22 |
| R12-07 | NFCTool | CardManager.refreshCard never clears lastError on success — stale error banner persists |
軽微 | ✅ Fixed 2026-04-22 |
| R12-08 | NFCTool | CardManager.suggestUsername returns already-taken base name when all 20 candidates exhausted |
中 | ✅ Fixed 2026-04-22 |
| R12-09 | NFCTool | checkUsernameAvailable catch-all returns true — network timeout looks like "available" |
中 | 🔵 Accepted (three-state requires API contract change; tracked in issues.md) |
| R12-10 | NFCTool | OneShotLocationDelegate.keepAlive static var — concurrent calls from two CardEditorView instances leak continuation |
軽微 | 🔵 Accepted (not reproducible in current nav structure) |
| R12-11 | NFCTool | syncWithServer has no in-flight guard — .task + willEnterForeground can fire concurrently on cold launch |
軽微 | 🔵 Accepted (@MainActor serializes; redundant calls harmless) |
| R12-12 | NFCTool | StoreManager.observeTransactionUpdates loop cancellation — tiny window where consumable finish() may be skipped |
軽微 | 🔵 Accepted (StoreKit singleton never deinit'd in practice) |
Round 11 — 2026-04-21 (Multi-Agent Re-audit: New Files)
| ID | Repo | Description | Status |
|---|---|---|---|
| B-06 | NFCTool | ToolsView navigationTitle sns.navigationTitle 誤キー |
✅ Fixed 2026-04-21 |
| B-12 | NFCTool | WiFiSavedListView navigationTitle sns.navigationTitle 誤キー |
✅ Fixed 2026-04-21 |
| B-13 | NFCTool | PaywallView ハードコード "NFC SNS CARD MAKER Pro" |
✅ Fixed 2026-04-21 |
| B-17 | NFCTool | BusinessRootView navigationTitle sns.navigationTitle 誤キー |
✅ Fixed 2026-04-21 |
| B-18 | NFCTool | CompanyWriteView navigationTitle sns.navigationTitle 誤キー |
✅ Fixed 2026-04-21 |
| B-03 | NFCTool | BusinessRootView デフォルトエントリ名「公式サイト」ハードコード |
✅ Fixed 2026-04-21 |
| C-02 | NFCTool | ToolFormHelpers.buildWiFiURL force unwrap URLComponents |
✅ Fixed 2026-04-21 |
| D-05 | NFCTool | CardEditorView.fetchCurrentAddress ビュー離脱でcontinuationリーク |
✅ Fixed 2026-04-21 |
| A-16 | NFCTool | CardKeychain.set SecItemAdd失敗を無視 |
✅ Fixed 2026-04-21 (DEBUG log) |
| WriteView | NFCTool | WriteView navigationTitle sns.navigationTitle 誤キー |
✅ Fixed 2026-04-21 |
| A-05 | NFCTool | StoreManager.purchase purchaseInFlight レース |
🔵 N/A (@MainActor serializes) |
| D-01 | NFCTool | TeamAddMemberView 保存ボタン連打 |
🔵 N/A (saving guard prevents re-entry) |
| D-04 | NFCTool | CardEditorView 位置情報権限なし時のサイレント失敗 |
🔵 Accepted (requestWhenInUseAuthorization + error toast in didFailWithError) |
| D-14 | NFCTool | TeamPaywallView 購入中の再タップ |
🔵 N/A (purchaseInFlight guard) |
| A-14 | NFCTool | CardManager refreshCard/restoreFromServer 同時実行 |
🔵 N/A (syncWithServer branches are mutually exclusive) |
| D-17 | NFCTool | CardUsernamePickerView タイムアウトなし |
🔵 N/A (SupabaseCardClient timeout fixed in R10) |
Round 10 — 2026-04-21 (Multi-Agent Full Scan: A/B/C/D/E)
| ID | Repo | Description | Status |
|---|---|---|---|
| B-05 | NFCTool | SettingsView 「管理ダッシュボード」ハードコード日本語 |
✅ Fixed 2026-04-21 |
| B-02 | NFCTool | CardModeRootView TextField placeholder "nextcode" ハードコード |
✅ Fixed 2026-04-21 |
| A-08 | NFCTool | CardManager.suggestUsername suffixが2桁以上でprefixLenが負になる |
✅ Fixed 2026-04-21 |
| A-07 | NFCTool | CardManager.refreshCard 失敗時にlastError未設定 |
✅ Fixed 2026-04-21 |
| D-17 | NFCTool | SupabaseCardClient URLRequest timeout未設定(60秒デフォルト) |
✅ Fixed 2026-04-21 (15s) |
| D-04 | NFCTool | PlaceSearchView Google Maps JS読み込み失敗時にonFailed永遠に呼ばれない |
✅ Fixed 2026-04-21 (10s timeout) |
| D-13 | NFCTool | NFCTagLabelSync タイムアウト時のエラーメッセージが「Decode failed」と誤表示 |
✅ Fixed 2026-04-21 |
| B-11 | NFCTool | TapHistoryView 「読み込み中...」「まだタップされていません」ハードコード日本語 |
✅ Fixed 2026-04-21 |
| D-11 | NFCTool | TapHistoryView 日付パース失敗でエントリが黙って消える |
✅ Fixed 2026-04-21 (DEBUG log) |
| B-13 | NFCTool | TeamModeRootView メンバーグリッドセルにタップフィードバックなし |
✅ Fixed 2026-04-21 |
| A-09 | NFCTool | TeamManager memberTokens Keychainデコード失敗でサイレントデータロス |
✅ Fixed 2026-04-21 |
| A-05 | NFCTool | AuthManager.restoreSession タイムアウトが先に返るとauth成功しても失敗扱い |
🔵 Accepted (withTaskGroup race; actual auth data already set before first next()) |
| A-01 | NFCTool | NFCCopier delayed destination tap task leak |
🔵 Accepted (state guard prevents damage; asyncAfter duration is 1.2s) |
| D-14 | NFCTool | WiFiRecordStore concurrent add() Keychain競合 |
🔵 N/A (@MainActor guarantees serial access) |
| D-06 | NFCTool | AnalysisView バック中NFCセッション継続 |
🔵 N/A (deinit calls invalidate()) |
| C-01 | NFCTool | ToolFormView God object (30+ @State) |
🔵 Accepted (planned refactor, no runtime bug) |
| C-05 | NFCTool | UserDefaults keys scattered, collision risk | 🔵 Accepted (audited, no current collision) |
| B-14 | NFCTool | AccountView エラー表示にdismissアフォーダンスなし |
🔵 Accepted (mode change clears error; close button available) |
Round 9 — 2026-04-21 (Final Edge Case Pass)
| ID | Repo | Description | Status |
|---|---|---|---|
| R9-D-03 | NFCTool | errorClearTask fired 6s after write failure, clearing subsequent success message |
✅ Fixed 2026-04-21 |
| R9-D-04 | NFCTool | CopyFlowView .sourceRead retry button missing .disabled(isBusy) guard |
✅ Fixed 2026-04-21 |
| R9-D-08 | NFCTool | HistoryManager.save() re-entrancy when iCloud notification fires during save |
✅ Fixed 2026-04-21 |
| R9-D-07 | NFCTool | StoreManager Bearer token missing !isEmpty check before IAP sync |
✅ Fixed 2026-04-21 |
| R9-A-01 | NFCTool | isCreatingTag stuck on Task cancellation |
✅ N/A — CancellationError caught by generic catch { isCreatingTag = false } |
| R9-A-04 | NFCTool | Tab switch during NFC write orphans session | 🔵 Accepted — CoreNFC session persists independently; system dialog handles timeout |
Critical
| ID | Repo | Description | Status |
|---|---|---|---|
| A-05 | NFCTool | Double-tap race in performReviewWrite() — isCreatingTag guard already present |
✅ (pre-existing guard) |
| D-04 | ReviewTap | Empty redirect URL saves without validation in TagDetailView | ✅ Fixed 2026-04-21 |
| D-05 | Both | write(url:) skips NFCNDEFReaderSession.readingAvailable check |
✅ (already guarded in startSession) |
| C-01 | ReviewTap | Write button missing isShortening in disable guard |
✅ Fixed 2026-04-21 |
Medium
| ID | Repo | Description | Status |
|---|---|---|---|
| A-01 | Both | lockTagOnly() doesn't reset isSuccess |
✅ Fixed 2026-04-21 |
| C-05 | Both | trialKeychainKey and trialStartKey identical; migration runs repeatedly |
✅ Fixed 2026-04-21 |
| D-06 | Both | isWriting stuck true after app backgrounded mid-session |
✅ Fixed 2026-04-21 |
| A-02 | Both | isShortening stuck true after view dealloc |
✅ Fixed 2026-04-21 |
| B-06 | Both | ResultBanner never auto-clears, stale failure message persists | ✅ Fixed 2026-04-21 |
| D-02 | ReviewTap | Concurrent load() from pull-to-refresh + toolbar button |
✅ Fixed 2026-04-21 |
| D-03 | ReviewTap | Optimistic multi-delete partial failure with silent data loss | ✅ Fixed 2026-04-21 |
| D-07 | ReviewTap | Dashboard silent zero state on stats API failure | ✅ Fixed 2026-04-21 |
| D-11 | Both | 15-second spinner on session restore with stale token + offline | ✅ Fixed 2026-04-21 |
| A-04 | NFCTool | Stale 3-second clear timer races in BusinessGroupDetailView | ✅ Fixed 2026-04-21 |
| D-09 | nfc-bz | IAP revocation no-op for credit products | ✅ Fixed 2026-04-21 |
| B-04 | NFCTool | RecordFixSheet auto-dismisses (2.5s) before user can verify | ✅ Fixed 2026-04-21 |
| C-04 | Both | UserDefaults keys duplicated verbatim across apps | ✅ Fixed 2026-04-21 |
| D-01 | ReviewTap | Stale error banner after successful reload in MyTagsView | ✅ N/A (load() clears loadError at start) |
Low
| ID | Repo | Description | Status |
|---|---|---|---|
| B-01 | Both | No spinner during isCreatingTag phase in Google Review write |
✅ Fixed 2026-04-21 |
| B-03 | NFCTool | HistoryView re-write button not disabled during active NFC session | ✅ Fixed 2026-04-21 |
| A-06 | NFCTool | addMemberForEntry stale async capture |
✅ Fixed 2026-04-21 (struct is value type; capture now explicit) |
| A-07 | NFCTool | RecordFixSheet dismiss timer not cancellable; onWriteSuccess never called | ✅ Fixed 2026-04-21 |
| B-09 | NFCTool | Location quick-rewrite timing | ✅ N/A (isValid guard requires geocoded URL in input2) |
| C-03 | Both | creditWasDeducted declared but no consumeCredit path exists |
✅ Fixed 2026-04-21 (removed dead variable) |
| C-06 | Both | app_source magic string not typed |
✅ Fixed 2026-04-21 (SupabaseClient.appSource constant) |
| A-09 | NFCTool | Location tool migration zeroes input1 |
✅ Fixed 2026-04-21 |
| B-05 | NFCTool | AnalysisView hero + error banner coexist with no clear failed state | ✅ Fixed 2026-04-21 |
| B-04 | NFCTool | RecordFixSheet auto-dismisses (2.5s) before user can verify | ✅ Fixed by A-07 |
Round 8 — 2026-04-21 (Final Sweep)
| ID | Repo | Description | Status |
|---|---|---|---|
| R8-02 | NFCTool | NFCToolApp widget time strings hardcoded Japanese — now uses RelativeDateTimeFormatter |
✅ Fixed 2026-04-21 |
| R8-01 | NFCTool | ContentView asyncAfter "strong self" — false positive (struct, no memory issue) | ✅ N/A |
| R8-03 | NFCTool | BusinessGroupDetailView asyncAfter "strong self" — struct, no memory issue | ✅ N/A |
| R8-04 | NFCTool | QRCodeGenerator extent.isFinite guard — CIFilter output is always finite | ✅ N/A |
| R8-05 | NFCTool | AccountView submitting stale — defer { submitting = false } already handles | ✅ N/A |
Round 7 — 2026-04-21 (Multi-Agent R7 Audit)
| ID | Repo | Description | Status |
|---|---|---|---|
| R7-A-01 | NFCTool | TapHistoryView HTTP errors (401/403/5xx) all showed same generic message |
✅ Fixed 2026-04-21 |
| R7-B-03 | NFCTool | WiFiPhotoPicker .onAppear auto-opened camera every time sub-sheet dismissed |
✅ Fixed 2026-04-21 |
| R7-C-01 | NFCTool | SNSGridEditorView Pro gate on Done button allowed free users to persist edits |
✅ Fixed 2026-04-21 (gate moved to each operation) |
| R7-D-02 | NFCTool | LocationManager.requestOnce() had no re-entry guard — rapid tap sent two requests |
✅ Fixed 2026-04-21 |
| R7-D-04 | NFCTool | NFCShortener no URL length limit and no empty-id validation on response |
✅ Fixed 2026-04-21 |
| R7-thread | NFCTool | SNSIDHistory + SNSGridOrder not @MainActor but published state updated on background thread |
✅ Fixed 2026-04-21 |
| R7-B-i18n | NFCTool | TapHistoryView relative/absolute times hardcoded Japanese |
✅ Fixed 2026-04-21 (RelativeDateTimeFormatter) |
| R7-A-02 | NFCTool | SNSGridEditorView done button paywall dismissed editor without saving |
✅ Fixed by R7-C-01 approach |
| R7-C-02 | NFCTool | WiFiRecordStore Keychain write failure silently ignored |
🔵 Accepted (SecItemAdd failure = no data loss; load() silently returns, next launch re-attempts) |
| R7-D-03 | NFCTool | Keychain thread safety (delete+add without mutex) | 🔵 Accepted (Keychain API is internally serialized on Darwin) |
| R7-C-03 | NFCTool | nfc.bz/api base URL duplicated in 3 files |
🔵 Accepted (all private static; shared in spirit) |
Round 6 — 2026-04-21 (Multi-Agent R6 Audit)
| ID | Repo | Description | Status |
|---|---|---|---|
| R6-D-06 | NFCTool | NFCAnalyzer.analyze() had no re-entry guard — rapid tap started two sessions |
✅ Fixed 2026-04-21 |
| R6-D-15 | NFCTool | CopyFlowView idle button never applied isBusy guard — double-tap possible |
✅ Fixed 2026-04-21 |
| R6-D-11 | NFCTool | WidgetSharedData.updateSNSLinks silently skipped failed serialize without logging |
✅ Fixed 2026-04-21 |
| R6-C-07 | NFCTool | HistoryManager cloud trim loop used 10% drop rate — convergence risk for large items |
✅ Fixed 2026-04-21 (now 20%) |
| R6-D-13 | NFCTool | HistoryManager.mergeItems ignored UUID conflicts — newer-wins resolution added |
✅ Fixed 2026-04-21 |
| R6-B-09 | NFCTool | HistoryView ResultBanner used overlay with fixed padding instead of safeAreaInset |
✅ Fixed 2026-04-21 |
| R6-B-05 | NFCTool | PaywallView product loading state indistinguishable (error shown as ProgressView) |
✅ Fixed 2026-04-21 |
| R6-A-11 | NFCTool | NFC session stale callback on rapid startSession — session ref mismatch acceptable at CoreNFC level | 🔵 Accepted (CoreNFC handles session uniqueness) |
| R6-D-01 | NFCTool | IAP credit purchase re-entry race window — finish() + UserDefaults window accepted | 🔵 Accepted (window ~1ms; StoreKit dedup catches replay) |
| R6-D-02 | NFCTool | AuthManager timeout race leaves tokens if auth was slow + offline | 🔵 Accepted (next launch retries; tokens expire anyway) |
| R6-C-01 | NFCTool | CompanyEditorView (842 lines) God object with duplicated search result UI | 🔵 Accepted (refactor planned, not a runtime bug) |
| R6-D-03 | NFCTool | Offline isPro cache doesn't check subscription expiry date | 🔵 Accepted (7-day cache max age limits exposure) |
| R6-D-08 | NFCTool | Location tool resolvedAddress lost on back button (not persisted) | 🔵 Low — user can re-geocode easily |
| R6-D-17 | NFCTool | NFCTagLabelSync timeout not surfaced in Settings UI | 🔵 Low — silent sync, retry on next launch |
Round 5 — 2026-04-21 (Security + Write Accuracy + UI Audit)
| ID | Repo | Description | Status |
|---|---|---|---|
| R5-A-01 | NFCTool | BusinessGroupDetailView team token saved in UserDefaults (plaintext) — security risk |
✅ Fixed 2026-04-21 |
| R5-A-02 | NFCTool | SimpleWriteView history rewrite recorded wrong URL (fullURL instead of actually-written URL) |
✅ Fixed 2026-04-21 |
| R5-A-05 | NFCTool | RecordFixSheet write button label didn't show spinner during isShortening |
✅ Fixed 2026-04-21 |
| R5-B-01 | NFCTool | HistoryView rewrite button enabled during isShortening — double-tap possible |
✅ Fixed 2026-04-21 |
| R5-B-02 | NFCTool | FriendCardView navigationTitle was sns.navigationTitle (wrong) |
✅ Fixed 2026-04-21 |
| R5-B-03 | NFCTool | AnalysisView navigationTitle was sns.navigationTitle (wrong) |
✅ Fixed 2026-04-21 |
| R5-B-04 | NFCTool | ToolFormView .url tool called writeURI() — never went through shortener |
✅ Fixed 2026-04-21 |
| R5-C-02 | NFCTool | BusinessGroupDetailView dead variable creditWasDeducted |
✅ Fixed 2026-04-21 |
| R5-C-03 | NFCTool | HistoryView section titles hardcoded Japanese (not localized) |
✅ Fixed 2026-04-21 |
| R5-D-01 | NFCTool | SimpleWriteView history quick-rewrite bypassed Pro access check |
✅ Fixed 2026-04-21 |
| R5-A-04 | NFCTool | fetchTapStatsForWidget creates ISO8601DateFormatter per loop iteration |
🔵 Accepted (minor perf) |
| R5-C-04 | NFCTool | HistoryView.displayTitle hardcoded Japanese ("nfc.bz ショートURL") |
🔵 Accepted (domain name, not user copy) |
| R5-C-05 | NFCTool | Widget time-ago strings hardcoded Japanese in fetchTapStatsForWidget |
🔵 Accepted (pending i18n pass) |
Round 4 — 2026-04-21 (Write Counting Full Audit)
| ID | Repo | Description | Status |
|---|---|---|---|
| NEW-01 | NFCTool | SearchWriteView: onChange(of: isSuccess) → onChange(of: message); shortenAs: .review → .url |
✅ Fixed 2026-04-21 |
| NEW-02 | NFCTool | FriendCardView: no history.add, no onChange, no shortener — QR-scanned writes never counted |
✅ Fixed 2026-04-21 |
| NEW-03 | NFCTool | CardWriteView: no history.add, no onChange, no disabled guard during write |
✅ Fixed 2026-04-21 |
| NEW-04 | NFCTool | TeamMemberDetailView: no history.add, no onChange, no disabled guard |
✅ Fixed 2026-04-21 |
| NEW-05 | NFCTool | AnalysisView RecordFixSheet: writeURI() for http(s) never went through shortener; onChange(isSuccess) → onChange(message) |
✅ Fixed 2026-04-21 |
| NEW-06 | NFCTool | All views using onChange(of: isSuccess) — consecutive writes silently dropped (isSuccess stays true) |
✅ Fixed 2026-04-21 (all views) |
Round 3 — 2026-04-21
| ID | Repo | Description | Status |
|---|---|---|---|
| A-10 | NFCTool | rewritingURL not cleared on consecutive rewrites — onChange(of: isSuccess) doesn't fire when isSuccess stays true |
✅ Fixed 2026-04-21 |
| A-11 | NFCTool | restoreSession always waits 5 s — deadline/sleep order was reversed |
✅ Fixed 2026-04-21 |
| A-12 | NFCTool | loadTeamForGroup catch: team?.id.isEmpty check — investigated, behavior is correct |
✅ N/A |
| B-10 | NFCTool | HistoryView.navigationTitle used wrong localization key sns.navigationTitle |
✅ Fixed 2026-04-21 |
| B-11 | NFCTool | ResultBanner in HistoryView overlaps system tab bar |
✅ Fixed 2026-04-21 |
| B-12 | NFCTool | onChange(of: isValid) auto-scrolls to QR mid-typing for non-plainText tools |
🔵 Accepted (minor; only triggers once when first valid) |
| B-13 | NFCTool | Custom tab bar "Company URL" tab calls onBack() — should be non-interactive active indicator |
✅ Fixed 2026-04-21 |
| C-07 | NFCTool | HistoryView.navigationTitle wrong key (same as B-10) |
✅ Fixed 2026-04-21 (same fix) |
| C-08 | NFCTool | WriteEventUploader.migrateICloudHistoryIfNeeded blocked-scheme guard duplicated |
✅ Fixed 2026-04-21 |
| C-09 | NFCTool | consumeCredit() always returns true but credit UI still shows count |
🔵 By design (all features free during rollout) |
| C-10 | NFCTool | ToolFormView has two onAppear closures — ordering ambiguity |
✅ Fixed 2026-04-21 |
| D-12 | NFCTool | HistoryManager.save() iCloud KVS trim loop — infinite loop risk on encode failure |
✅ Fixed 2026-04-21 |
| D-13 | NFCTool | WriteEventUploader.enqueue() Task captures singleton strongly |
🔵 Singleton pattern; acceptable |
| D-14 | NFCTool | migrateExistingMembersToDefaultEntry race with network arrival |
🔵 Migration flag prevents double-run; acceptable |
| D-15 | NFCTool | isCreatingTag not reset if Task cancelled mid-flight |
✅ Already handled by scenePhase == .background reset |