Skip to content

Commit 15869a3

Browse files
committed
docs: DevLog 아키텍처 플로우 문서화
AI 하네스의 요청 처리 흐름을 Mermaid로 정리 모듈 레이어 맵과 Core, Domain 판단 흐름 정의 외부 의존성, Widget 데이터, 검증 플로우 기준 추가
1 parent e3bcdaf commit 15869a3

1 file changed

Lines changed: 300 additions & 0 deletions

File tree

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
# DevLog Architecture Flow
2+
3+
## Purpose
4+
5+
This reference defines the DevLog-specific harness flow for AI-assisted architecture work.
6+
7+
The goal is not to make the AI decide more architecture policy. The goal is to make the AI stop before it makes project-specific architecture decisions that should be confirmed by the user.
8+
9+
Use this reference with `AGENTS.md` and `.hermes/skills/devlog-architecture-harness/SKILL.md`.
10+
11+
This repository is an Xcode workspace-based modular iOS app. There is no root `Package.swift`; modules are separate `.xcodeproj` entries under `DevLog.xcworkspace`.
12+
13+
## High-level harness flow
14+
15+
```mermaid
16+
flowchart TD
17+
Request["User request"]
18+
LoadRules["Load DevLog rules"]
19+
LoadContext["Inspect current repository context"]
20+
Classify["Classify change type"]
21+
Boundary["Check layer boundary"]
22+
Ambiguous{"Architecture boundary ambiguous?"}
23+
Ask["Ask user before editing"]
24+
Plan["Prepare narrow edit plan"]
25+
Edit["Apply scoped change"]
26+
Verify["Verify build or docs state"]
27+
Diff["Inspect final diff scope"]
28+
Record["Report decision and result"]
29+
30+
Request --> LoadRules
31+
LoadRules --> LoadContext
32+
LoadContext --> Classify
33+
Classify --> Boundary
34+
Boundary --> Ambiguous
35+
Ambiguous -->|Yes| Ask
36+
Ask --> Boundary
37+
Ambiguous -->|No| Plan
38+
Plan --> Edit
39+
Edit --> Verify
40+
Verify --> Diff
41+
Diff --> Record
42+
```
43+
44+
## Change classification
45+
46+
```mermaid
47+
flowchart TD
48+
Change["Requested change"]
49+
ImportOnly{"Only import/access fallout?"}
50+
BoundaryMove{"Moves ownership or dependency?"}
51+
SDKPlacement{"Changes external SDK placement?"}
52+
DIChange{"Changes assembler or DI ownership?"}
53+
WidgetFlow{"Changes widget data flow?"}
54+
Mechanical["Mechanical change"]
55+
Architecture["Architecture change"]
56+
Ambiguous["Ambiguous change"]
57+
58+
Change --> ImportOnly
59+
ImportOnly -->|Yes| Mechanical
60+
ImportOnly -->|No| BoundaryMove
61+
BoundaryMove -->|Yes| Architecture
62+
BoundaryMove -->|No| SDKPlacement
63+
SDKPlacement -->|Yes| Architecture
64+
SDKPlacement -->|No| DIChange
65+
DIChange -->|Yes| Architecture
66+
DIChange -->|No| WidgetFlow
67+
WidgetFlow -->|Yes| Architecture
68+
WidgetFlow -->|No| Ambiguous
69+
```
70+
71+
## DevLog layer map
72+
73+
```mermaid
74+
flowchart TD
75+
App["DevLogApp\nComposition root\nApp lifecycle\nAssembler wiring"]
76+
Presentation["DevLogPresentation\nSwiftUI views\nViewModels\nCoordinators\nUI state"]
77+
Domain["DevLogDomain\nEntities\nRepository protocols\nUse cases"]
78+
Data["DevLogData\nRepository implementations\nDTOs\nMappers\nService/store protocols"]
79+
Infra["DevLogInfra\nFirebase\nSocial login\nNetwork\nLink metadata\nMessaging"]
80+
Persistence["DevLogPersistence\nUserDefaults\nImage store\nWidget snapshot persistence"]
81+
Core["DevLogCore\nDI\nLogger\nShared value/query types\nWidget snapshot values"]
82+
WidgetCore["DevLogWidgetCore\nWidget snapshot models\nFactories\nApp Group constants"]
83+
WidgetExtension["DevLogWidgetExtension\nWidgetKit UI\nProviders\nTimelines"]
84+
85+
App --> Presentation
86+
App --> Domain
87+
App --> Data
88+
App --> Infra
89+
App --> Persistence
90+
App --> Core
91+
App --> WidgetCore
92+
93+
Presentation --> Domain
94+
Presentation --> Core
95+
96+
Domain --> Core
97+
98+
Data --> Domain
99+
Data --> Core
100+
101+
Infra --> Data
102+
Infra --> Core
103+
104+
Persistence --> Data
105+
Persistence --> Core
106+
Persistence --> WidgetCore
107+
108+
WidgetExtension --> WidgetCore
109+
WidgetCore --> Core
110+
```
111+
112+
## Boundary rules
113+
114+
| Layer | Owns | Allowed direction | Ask before |
115+
| --- | --- | --- | --- |
116+
| `DevLogCore` | DI primitives, logger, shared value/query types, widget snapshot values | No DevLog layer dependency | Moving domain entities into Core |
117+
| `DevLogDomain` | entities, repository protocols, use cases | Core only | Adding Data, Infra, Persistence, Presentation, App, Widget UI, or SDK dependency |
118+
| `DevLogData` | repository implementations, DTOs, mappers, data protocols | Domain, Core | Adding concrete Firebase, WidgetKit, storage, or platform implementation details |
119+
| `DevLogInfra` | Firebase, social login, network, metadata, messaging implementations | Data, Core | Moving SDK-specific behavior out of Infra |
120+
| `DevLogPersistence` | local stores, image cache, widget snapshot persistence | Data, Core, WidgetCore | Moving domain logic or remote SDK behavior into Persistence |
121+
| `DevLogPresentation` | UI, view models, coordinators, presentation state | Domain, Core | Adding Data, Infra, Persistence, or App dependency |
122+
| `DevLogApp` | composition root, lifecycle, assembler wiring | Concrete app layers | Moving feature logic into App |
123+
| `DevLogWidgetCore` | widget data contracts and pure snapshot logic | Core | Adding Domain, Data, Infra, Persistence, Presentation, or App dependency |
124+
| `DevLogWidgetExtension` | WidgetKit rendering and timeline plumbing | WidgetCore | Calling app/domain services directly |
125+
126+
## Presentation Store flow
127+
128+
```mermaid
129+
flowchart LR
130+
View["SwiftUI View"]
131+
ViewModel["ViewModel / Store"]
132+
Send["send(Action)"]
133+
Reduce["reduce(with:)"]
134+
State["State update"]
135+
SideEffect["SideEffect"]
136+
Run["run(SideEffect)"]
137+
Service["Injected use case or service"]
138+
139+
View --> ViewModel
140+
ViewModel --> Send
141+
Send --> Reduce
142+
Reduce --> State
143+
Reduce --> SideEffect
144+
SideEffect --> Run
145+
Run --> Service
146+
Service --> Send
147+
```
148+
149+
Preserve this flow unless the user explicitly asks to change the Presentation architecture. Reducers compute state and return side effects. I/O belongs in `run` or injected services.
150+
151+
## Ambiguity gate
152+
153+
The AI must stop and ask the user when it reaches any of these points.
154+
155+
```mermaid
156+
flowchart TD
157+
Check["Architecture decision needed"]
158+
CoreDomain{"Core vs Domain ownership?"}
159+
Shared{"Moved only because shared?"}
160+
NewDependency{"New module dependency?"}
161+
ExternalSDK{"External SDK crosses layer?"}
162+
WidgetBoundary{"WidgetCore sees app/domain/data?"}
163+
BuildShortcut{"Build fix relaxes boundary?"}
164+
ScopeDrift{"Outside current task scope?"}
165+
Ask["Ask user before editing"]
166+
Proceed["Proceed with scoped edit"]
167+
168+
Check --> CoreDomain
169+
CoreDomain -->|Yes| Ask
170+
CoreDomain -->|No| Shared
171+
Shared -->|Yes| Ask
172+
Shared -->|No| NewDependency
173+
NewDependency -->|Yes| Ask
174+
NewDependency -->|No| ExternalSDK
175+
ExternalSDK -->|Yes| Ask
176+
ExternalSDK -->|No| WidgetBoundary
177+
WidgetBoundary -->|Yes| Ask
178+
WidgetBoundary -->|No| BuildShortcut
179+
BuildShortcut -->|Yes| Ask
180+
BuildShortcut -->|No| ScopeDrift
181+
ScopeDrift -->|Yes| Ask
182+
ScopeDrift -->|No| Proceed
183+
```
184+
185+
## Core vs Domain decision flow
186+
187+
Use this flow when deciding whether a type belongs in Core or Domain.
188+
189+
```mermaid
190+
flowchart TD
191+
Type["Type under review"]
192+
DomainMeaning{"Represents business/domain meaning?"}
193+
QueryOrPrimitive{"Generic query, option, logger, DI, or shared primitive?"}
194+
UsedByWidget{"Needed by WidgetCore snapshot contract?"}
195+
OnlyShared{"Only reason is multiple modules need it?"}
196+
Domain["Keep or place in DevLogDomain"]
197+
Core["Keep or place in DevLogCore"]
198+
Ask["Ask user"]
199+
200+
Type --> DomainMeaning
201+
DomainMeaning -->|Yes| Domain
202+
DomainMeaning -->|No| QueryOrPrimitive
203+
QueryOrPrimitive -->|Yes| Core
204+
QueryOrPrimitive -->|No| UsedByWidget
205+
UsedByWidget -->|Yes| Core
206+
UsedByWidget -->|No| OnlyShared
207+
OnlyShared -->|Yes| Ask
208+
OnlyShared -->|No| Ask
209+
```
210+
211+
## External dependency flow
212+
213+
Use this flow before introducing or moving imports such as Firebase, GoogleSignIn, AuthenticationServices, UserNotifications, LinkPresentation, Network, or WidgetKit.
214+
215+
```mermaid
216+
flowchart TD
217+
Import["External framework import"]
218+
Firebase{"Firebase/Auth/Firestore/Functions/Messaging?"}
219+
SocialLogin{"GoogleSignIn or AuthenticationServices login implementation?"}
220+
NetworkMeta{"Network or LinkPresentation implementation?"}
221+
WidgetKit{"WidgetKit?"}
222+
Infra["Prefer DevLogInfra"]
223+
Persistence["Allow only for widget snapshot update/persistence if already established"]
224+
WidgetExtension["Allow in DevLogWidgetExtension rendering/timeline code"]
225+
Ask["Ask user before crossing layer"]
226+
227+
Import --> Firebase
228+
Firebase -->|Yes| Infra
229+
Firebase -->|No| SocialLogin
230+
SocialLogin -->|Yes| Infra
231+
SocialLogin -->|No| NetworkMeta
232+
NetworkMeta -->|Yes| Infra
233+
NetworkMeta -->|No| WidgetKit
234+
WidgetKit -->|Widget UI| WidgetExtension
235+
WidgetKit -->|Snapshot update already established| Persistence
236+
WidgetKit -->|Other| Ask
237+
```
238+
239+
## Widget data-flow boundary
240+
241+
```mermaid
242+
flowchart LR
243+
App["App runtime\nDomain/Data/Infra/Persistence"]
244+
Snapshot["Snapshot generation\nPersistence + WidgetCore"]
245+
AppGroup["App Group storage\nShared defaults"]
246+
WidgetCore["WidgetCore\nSnapshot models\nFactories"]
247+
WidgetExtension["Widget extension\nWidgetKit UI"]
248+
249+
App --> Snapshot
250+
Snapshot --> AppGroup
251+
AppGroup --> WidgetCore
252+
WidgetCore --> WidgetExtension
253+
```
254+
255+
Widget UI should consume snapshot data. It should not fetch app services or domain repositories directly.
256+
257+
## Verification flow
258+
259+
```mermaid
260+
flowchart TD
261+
Changed["Files changed"]
262+
Swift{"Swift/iOS project code changed?"}
263+
Docs{"Docs or harness only?"}
264+
Xcode["Build with Xcode Local MCP"]
265+
Diff["Inspect git diff scope"]
266+
NoBuild["No iOS build required"]
267+
Report["Report verification result"]
268+
269+
Changed --> Swift
270+
Swift -->|Yes| Xcode
271+
Swift -->|No| Docs
272+
Docs -->|Yes| NoBuild
273+
Docs -->|No| Diff
274+
Xcode --> Diff
275+
NoBuild --> Diff
276+
Diff --> Report
277+
```
278+
279+
## Required working notes
280+
281+
Before editing architecture code, the AI should be able to answer these questions:
282+
283+
1. What layer owns the changed concept today?
284+
2. What layer should own it after the change?
285+
3. Which imports prove the current dependency direction?
286+
4. Which target dependency will change?
287+
5. Does the change expose an external SDK outside its current boundary?
288+
6. Does the change affect WidgetCore or WidgetExtension boundaries?
289+
7. Is this change inside the current issue or PR scope?
290+
8. Is user confirmation required before editing?
291+
292+
## Completion checklist
293+
294+
- DevLog-specific rules were loaded.
295+
- Current files and imports were inspected.
296+
- Ambiguous architecture decisions were confirmed by the user.
297+
- Swift logic was preserved unless explicitly approved.
298+
- Diff scope was checked.
299+
- Xcode Local MCP build was used for Swift/iOS code changes.
300+
- Docs-only or harness-only changes were reported as such, without claiming app build verification.

0 commit comments

Comments
 (0)