Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# AGENTS.md

## Scope

- This file is the only source of rules for the Codex GitHub code review bot in this repository. It is self-contained; do not look for rules in external documents.
- It applies to code review comments on pull requests. It does not describe how to implement or run the project.

## Code Review Rules

### Language (highest priority)

- Write every review comment, summary, and question in Korean (한국어). Do not write review text in English, even when the PR title, description, commit messages, or code comments are in English.
- Keep code identifiers, file paths, commands, and quoted source text in their original form.
- Keep the priority badge (`P0` to `P3`) as is, and write the explanation in Korean.
- Do not translate implementation names such as `StorePattern`, `ToastPresenter`, or `MainView`.
- Keep comments concise: one issue per comment, short sentences, and end sentences in Korean noun form where natural (명사형 종결), matching the existing PR style.
- Preferred shape of a comment:
- Title: `[P2] 인스펙터 안의 Todo 상세에서 뒤로가기가 옵션 페이지로 돌아가지 않음`
- Location, trigger, impact, and minimal fix, each on its own line in Korean.

### Priorities

- `P0`, `P1`: data loss, security, crashes, concurrency and resource lifetime problems, behavior regressions.
- `P2`: architecture violations, scope drift, missing meaningful tests.
- `P3`: low-impact violations of the repository conventions below.
- Do not report:
- Matters of taste and style that do not affect behavior.
- Formatting-only issues that SwiftLint would catch.
- Speculation that the code does not support.

### What counts as a finding

- Report only issues that the code supports and that have a concrete failure scenario.
- For each finding, include:
- The file path and line.
- The condition that triggers it.
- The impact on users or behavior.
- The minimum correction.
- Order findings by severity. Do not repeat the same finding in several places.
- Do not claim that tests or runtime behavior were verified if they were not run.
- Do not raise again threads that are already resolved or feedback that was already applied.

### Checks for this repository (DevLog_iOS)

- Behavior preservation:
- Behavior changes that were not requested, and cleanups outside the requested scope.
- New logic written where existing logic could have been reused.
- Architecture:
- No dependency injection between types of the same layer (initializer, stored property, environment, or runtime resolver).
- The only exception is a SwiftUI `View` in `Application/Presentation` receiving same-layer presentation objects such as a ViewModel, Coordinator, or Store.
- `Infra` depends on `Data` and `Core`, not on `Domain`.
- Do not move domain entities to `Core` only because several modules use them.
- Firebase-specific error detection belongs in `Infra`; `Data` handles domain-level errors after mapping.
- Widget UI consumes snapshot data only. `WidgetCore` must not depend on Domain, Data, Infra, Persistence, Presentation, or App.
- StorePattern:
- Keep `@MainActor`, `State`, `Action`, `SideEffect`, and `send -> reduce -> run`.
- Reducers compute state and return side effects; I/O belongs in `run` or injected services.
- Do not add task cancellation or async wrappers around operations that are not actually async.
- Remove reducer-era helper methods left behind after work moved into `run`.
- SwiftUI:
- No intermediate views, properties, or wrappers that only forward to another view.
- Feature-owned presentation state of a Store-backed view lives in the Feature's `State`; do not duplicate it as view-local `@State`.
- View-local `@State` is only for transient rendering and measurement that does not change the feature flow.
- Do not add accessibility code that was not requested, and do not remove existing accessibility behavior without a reason.
- Design tokens:
- UI changes must use the existing `ColorPalette` colors (`Color+Assets`), not hard-coded or system colors.
- Localization:
- Missing keys or translations for new user-facing strings.
- Large unrelated changes in `.xcstrings`.
- Swift conventions (`P3`):
- Explicit type annotations that are not required.
- New Swift file headers whose author is not `opfic`.
- Prefer `<` and `<=` over `>` and `>=` when the meaning is the same.
- Repository rules:
- SwiftUI preview code (`#Preview`, `PreviewProvider`, preview-only fixtures) included in a commit.
- Secrets, tokens, or private configuration files.
- Unnecessary changes to generated Xcode projects or `Package.resolved`.
- A PR title that differs from the title of the linked issue.
- A PR body with sections not declared in the PR template, or with verification results (build, test, lint).
- Tool attribution text (such as `Co-Authored-By` or `Generated with`) in commits or the PR body.

### Review summary

- Write the findings first, then, in order:
- Verdict: one of `Pass`, `Block`, `Needs Follow-up`.
- The scope that was inspected.
- Verification that could not be done.
- Limitations.
- `Pass` means no blocking finding in the inspected scope. It does not mean tests were run or runtime behavior was verified.
- If there are no findings, state in Korean that there are none ("지적 없음") together with the inspected scope.
- If required source files could not be read, state the missing prerequisite and do not give `Pass`.
- Final reminder: all of the above output must be written in Korean.
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ public struct GoalDetailView: View {
onUpdate: { store.send(.view(.refresh)) }
)
}
.navigationDestination(item: $store.scope(state: \.todoDetail, action: \.todoDetail)) {
.navigationDestination(
item: $store.scope(state: \.todoDetail, action: \.todoDetail),
interactivePop: true
) {
TodoDetailView(
store: Store(
initialState: TodoDetailFeature.State(
Expand Down
16 changes: 3 additions & 13 deletions Application/Presentation/Entry/Sources/Root/RootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,33 +53,23 @@ public struct RootView: View {
}
.prominentAlert(store, state: \.alert, action: \.alert)
.sheet(item: $store.scope(state: \.sheet, action: \.sheet)) { sheetStore in
sheetContent(todoId: sheetStore.todoId) {
sheetStore.send(.tapCloseButton)
}
sheetContent(todoId: sheetStore.todoId)
}
.onReceive(pushNotificationTodoIdPublisher) { todoId in
store.send(.presentTodoDetail(todoId))
clearPushNotificationRoute()
}
}

private func sheetContent(
todoId: String,
onClose: @escaping () -> Void
) -> some View {
private func sheetContent(todoId: String) -> some View {
NavigationStack {
TodoDetailView(store: Store(
initialState: TodoDetailFeature.State(todoId: todoId, showEditButton: false)
) {
TodoDetailFeature()
})
.toolbar {
ToolbarLeadingButton {
onClose()
}
}
}
.background(Color(.systemGroupedBackground))
.background(Color.appBackground)
.presentationDragIndicator(.visible)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public struct HomeView: View {
interactivePop: {
switch $0 {
case .category: true
case .todo: false
case .todo: true
}
},
destination: destinationView
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ struct SearchView: View {
}
.background(Color.appBackground.ignoresSafeArea())
.prominentAlert(store, state: \.alert, action: \.alert)
.navigationDestination(for: Path.self) { path in
.navigationDestination(for: Path.self, interactivePop: { _ in true }) { path in
switch path {
case .todo(let todoId):
TodoDetailView(store: Store(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,14 +288,9 @@ public struct PushNotificationListView: View {
) {
TodoDetailFeature()
})
.id(sheetStore.todoId)
.toolbar {
ToolbarLeadingButton {
sheetStore.send(.tapCloseButton)
}
}
.id(sheetStore.todoId)
}
.background(Color(.systemGroupedBackground))
.background(Color.appBackground)
.presentationDragIndicator(.visible)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,37 +9,59 @@ import SwiftUI
import Domain

struct TodoDetailContentView: View {
@ScaledMetric(relativeTo: .title3) private var fontSize = 20
let title: String
let content: String
let referenceItems: [Int: TodoReferenceItem]
var number: Int
var onOpenTodoID: ((String) -> Void)?

var body: some View {
ZStack {
Color(.systemGroupedBackground).ignoresSafeArea()
VStack(alignment: .leading, spacing: 10) {
ScrollView {
LazyVStack(alignment: .leading, spacing: 20) {
ScrollView(.horizontal) {
HStack(alignment: .firstTextBaseline, spacing: 8) {
Text(title)
Text("#\(number)")
.foregroundStyle(.gray)
.fixedSize(horizontal: true, vertical: false)
LazyHStack(alignment: .firstTextBaseline, spacing: 0, pinnedViews: .sectionFooters) {
Section {
Text(title)
} footer: {
Text("#\(number)")
.foregroundStyle(Color.textTertiary)
.padding(.leading, 8)
.background {
Color.appBackground.padding(.trailing, -16)
}
}
}
.lineLimit(1)
.font(.title3.bold())
}
.frame(height: fontSize + 10)
.scrollBounceBehavior(.always, axes: .horizontal)
.scrollIndicators(.hidden)
.contentMargins(16, for: .scrollContent)
Divider()
TodoMarkdownContentView(
content: content,
referenceItems: referenceItems,
onOpenTodoID: onOpenTodoID
)
.contentMargins(.horizontal, 16, for: .scrollContent)
.padding(.horizontal, -16)

Group {
if content.isEmpty {
ContentUnavailableView(
String(
localized: "development_record_content_empty_title",
bundle: PresentationResources.bundle
),
systemImage: "doc.text"
)
} else {
TodoMarkdownContentView(
content: content,
referenceItems: referenceItems,
isScrollEnabled: false,
onOpenTodoID: onOpenTodoID
)
.frame(minHeight: 120, alignment: .top)
}
}
.padding(.vertical, 16)
.background(Color.surface, in: .rect(cornerRadius: 24))
}
.padding()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct PreviewView: View {

var body: some View {
ZStack {
Color(.systemGroupedBackground).ignoresSafeArea()
Color.appBackground.ignoresSafeArea()
if let todo = store.todo {
TodoDetailContentView(
title: todo.title,
Expand Down
Loading
Loading