diff --git a/.agents/rules/architecture.md b/.agents/rules/architecture.md deleted file mode 100644 index 84cbc093..00000000 --- a/.agents/rules/architecture.md +++ /dev/null @@ -1,405 +0,0 @@ -# DevLog Architecture Rules - -## Purpose - -This reference defines the DevLog-specific flow and boundaries for AI-assisted architecture work. - -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. - -Use this reference with `AGENTS.md` and `.agents/rules/general.md`. - -This repository is a Tuist-generated, workspace-based modular iOS app. There is no root `Package.swift`; module projects are generated from `Workspace.swift` and each module's `Project.swift`. - -`ThirdParty` is an external package registry, not a DevLog application layer. It owns Swift Package declarations and product linkage only, has no DevLog target dependency, and any target including `Domain` may depend on it when that target imports an external package product. - -## When to use - -Read this file before work that changes any of these areas: - -- Module boundaries or file ownership across `Application/*`, `Libraries/*`, and `Widget/*` targets. -- Swift imports or Tuist target dependencies. -- DI graph wiring or same-layer dependency injection. -- Repository, service, store, or use case contracts. -- Firebase, social login, network, link metadata, notification, or WidgetKit dependency placement. -- Widget snapshot, App Group, or widget deep-link data flow. -- Architecture diagrams, README architecture text, or PR architecture explanations. - -Before editing, also read `README.md`. Read `.agents/rules/project-workflows.md` when the task involves PR review, commits, Xcode project files, CI, widgets, Store reducers, localization, release, or build tooling. - -Then inspect the concrete files, Swift imports, and Tuist target dependencies related to the requested change. Do not rely on layer names alone. - -## Mandatory flow - -1. Identify the changed layer and owning target before editing. -2. Inspect the current Swift import direction and Xcode target/framework dependency before deciding. -3. Classify the change as mechanical, architectural, or ambiguous. -4. Stop and ask the user before editing when the architecture boundary is ambiguous. -5. Keep the diff limited to the requested architecture scope. -6. Follow `.agents/rules/project-workflows.md` for verification after Swift or iOS project changes. -7. Report the changed files, architecture decision, verification result, and unresolved user decisions. - -## Safe mechanical changes - -These may proceed after inspection when they do not change architecture meaning: - -- Removing unused imports. -- Updating import statements after an already-approved file move. -- Fixing access control needed by an already-approved module boundary. -- Updating tests to match an already-approved public contract. -- Editing docs to reflect the current verified architecture. - -## High-level architecture flow - -```mermaid -flowchart TD - Request["User request"] - LoadRules["Load DevLog rules"] - LoadContext["Inspect current repository context"] - Classify["Classify change type"] - Boundary["Check layer boundary"] - Ambiguous{"Architecture boundary ambiguous?"} - Ask["Ask user before editing"] - Plan["Prepare narrow edit plan"] - Edit["Apply scoped change"] - Verify["Verify build or docs state"] - Diff["Inspect final diff scope"] - Record["Report decision and result"] - - Request --> LoadRules - LoadRules --> LoadContext - LoadContext --> Classify - Classify --> Boundary - Boundary --> Ambiguous - Ambiguous -->|Yes| Ask - Ask --> Boundary - Ambiguous -->|No| Plan - Plan --> Edit - Edit --> Verify - Verify --> Diff - Diff --> Record -``` - -## Change classification - -```mermaid -flowchart TD - Change["Requested change"] - ImportOnly{"Only import/access fallout?"} - BoundaryMove{"Moves ownership or dependency?"} - SDKPlacement{"Changes external SDK placement?"} - DIChange{"Changes assembler or DI ownership?"} - WidgetFlow{"Changes widget data flow?"} - Mechanical["Mechanical change"] - Architecture["Architecture change"] - Ambiguous["Ambiguous change"] - - Change --> ImportOnly - ImportOnly -->|Yes| Mechanical - ImportOnly -->|No| BoundaryMove - BoundaryMove -->|Yes| Architecture - BoundaryMove -->|No| SDKPlacement - SDKPlacement -->|Yes| Architecture - SDKPlacement -->|No| DIChange - DIChange -->|Yes| Architecture - DIChange -->|No| WidgetFlow - WidgetFlow -->|Yes| Architecture - WidgetFlow -->|No| Ambiguous -``` - -## DevLog layer map - -```mermaid -flowchart TD - App["App\nComposition root\nApp lifecycle\nCradle graph wiring"] - Presentation["Presentation\nSwiftUI views\nViewModels\nCoordinators\nUI state"] - Domain["Domain\nEntities\nRepository protocols\nUse cases"] - Data["Data\nRepository implementations\nDTOs\nMappers\nService/store protocols"] - Infra["Infra\nFirebase\nSocial login\nNetwork\nLink metadata\nMessaging"] - Persistence["Persistence\nUserDefaults\nImage store\nNon-widget app persistence"] - Widget["Widget\nApp-side widget bridge\nSync/session handlers\nSnapshot update orchestration\nWidgetKit reload bridge"] - Core["Core\nDI\nLogger\nShared value/query types\nLightweight widget values"] - WidgetCore["WidgetCore\nWidget snapshot models\nFactories\nApp Group constants"] - WidgetExtension["WidgetExtension\nWidgetKit UI\nProviders\nTimelines"] - MarkdownRenderer["MarkdownRenderer\nSwiftUI renderer API\nInternal WebKit bridge\nRenderer resources and Tooling"] - - App --> Presentation - App --> Domain - App --> Data - App --> Infra - App --> Persistence - App --> Core - App --> Widget - App --> WidgetCore - App -.-> WidgetExtension - - Presentation --> Domain - Presentation --> Core - Presentation --> MarkdownRenderer - - Domain --> Core - - Data --> Domain - Data --> Core - - Infra --> Data - Infra --> Core - - Persistence --> Data - Persistence --> Core - - Widget --> Data - Widget --> Core - Widget --> WidgetCore - - WidgetExtension --> WidgetCore - WidgetCore --> Core -``` - -## Boundary rules - -| Layer | Owns | Allowed direction | Ask before | -| --- | --- | --- | --- | -| `ThirdParty` | external package declarations, product linkage, marker sources | No DevLog target dependency; may be depended on by any target | Adding DevLog feature, service, adapter, or layer dependency; changing package versions or products outside the requested scope | -| `Core` | logger, shared value/query types, display options, activity kinds, lightweight widget bridge values | No DevLog layer dependency; `ThirdParty` when needed | Moving domain entities into Core | -| `Domain` | entities, repository protocols, use cases | Core, `ThirdParty` when needed | Adding Data, Infra, Persistence, Presentation, App, or Widget UI dependency | -| `Data` | repository implementations, DTOs, mappers, data protocols, widget repository/updater/sync contracts | Domain, Core, `ThirdParty` when needed | Adding WidgetKit, storage, WidgetCore snapshot model/factory usage, or platform implementation details; moving concrete widget handlers into Data | -| `Infra` | application infrastructure service implementations for social login, network, metadata, and messaging | Data, Core, `ThirdParty` when needed | Adding any Domain dependency or SDK service contract coupling | -| `Persistence` | local stores, image cache, non-widget app persistence | Data, Core, `ThirdParty` when needed | Adding WidgetCore, WidgetKit reload, Widget, widget snapshot generation, or widget bridge ownership | -| `Presentation` | UI, view models, coordinators, presentation state, narrow presentation-scoped platform side effects | Domain, Core, `ThirdParty` when needed | Adding Data, Infra, Persistence, or App dependency; expanding platform service ownership beyond UI-side effects | -| `MarkdownRenderer` | public SwiftUI renderer and reference value, internal WebKit bridge, renderer resources, TypeScript Tooling, renderer tests | system frameworks, `ThirdParty` when needed | Adding a DevLog application layer dependency, exposing WebKit bridge types, adding another Presentation importer, or re-exporting the module | -| `Widget` | app-side widget bridge, sync bus implementation, sync/session handlers, snapshot generation/persistence orchestration, WidgetKit reload bridge, provider graph | Data, Core, WidgetCore, `ThirdParty` when needed | Adding Domain, Infra, Persistence, Presentation, or App dependency | -| `App` | composition root, lifecycle, Cradle graph wiring, app target ownership for widget extension embedding | Concrete app layers, `ThirdParty` for framework linking | Moving feature logic into App | -| `WidgetCore` | widget snapshot models, factories, app-group keys/defaults store, deep links, pure snapshot logic | Core, `ThirdParty` when needed | Adding Domain, Data, Infra, Persistence, Presentation, App, or Widget dependency | -| `WidgetExtension` | WidgetKit rendering and timeline plumbing | WidgetCore, `ThirdParty` when needed | Calling app/domain services directly | - -## Presentation target structure - -- `Presentation` preserves `App -> Presentation` imports and re-exports the entry API through `Application/Presentation/Sources/**/*.swift`. -- `Entry` owns root, auth, login, main tab shell, window, and global route responsibilities. It owns the `Domain` references needed by those flows. -- `EntryTests` validates `Entry` through `Application/Presentation/Entry/Tests/**/*.swift`. -- `HomeTab`, `TodayTab`, `NotificationTab`, and `ProfileTab` remain tab-specific feature targets and each target owns the `Domain` references it needs. -- `PresentationShared` owns shared Todo, Search, Loading UI, and presentation contracts. -- `App` owns composition root, lifecycle, and Cradle graph wiring. It must not take ownership of presentation feature or root flows. - -## MarkdownRenderer module boundary - -- `Libraries/MarkdownRenderer` owns `MarkdownRendererView`, `MarkdownRendererReference`, the internal `MarkdownWebView` and Coordinator, URL/message policy, renderer resources, renderer tests, and TypeScript Tooling. -- `MarkdownRenderer` may depend on system frameworks such as `SwiftUI`, `WebKit`, `Foundation`, and `CoreGraphics`. It must not import `Core`, `Domain`, `Data`, `Infra`, `Persistence`, `Presentation`, `App`, `Widget`, or `WidgetCore`. -- `PresentationShared` depends on `MarkdownRenderer`. `Application/Presentation/PresentationShared/Sources/Common/TodoMarkdownContentView.swift` is the only direct Presentation importer and must not use `@_exported import MarkdownRenderer`. -- `TodoMarkdownContentView` owns `TodoReferenceItem` conversion, symbol image data URL creation, tab bar and safe-area adaptation, and `onOpenTodoID` callback adaptation. These DevLog concerns must not move into `MarkdownRenderer`. -- `MarkdownRendererView` owns color scheme, locale, external URL opening, scaled font size, and the public renderer input. The internal `MarkdownWebView` keeps `WKWebView` lifecycle, message handling, internal scroll ownership, and `obscuredContentInsets.bottom` handling out of the public API. -- `Libraries/MarkdownRenderer/Tooling` generates the tracked files under `Libraries/MarkdownRenderer/Resources/MarkdownRenderer`. CI must verify that generated and tracked resources remain synchronized. - -## Layer-internal dependency injection - -Do not inject dependencies between types that belong to the same layer. - -This rule covers initializer injection, stored-property injection, environment injection, and resolving same-layer types through a runtime resolver. - -The only allowed exception is a SwiftUI `View` file in `Application/Presentation` receiving same-layer presentation objects such as a ViewModel, Coordinator, or Store for UI composition. - -That exception does not apply to non-View files in Presentation, and does not apply to Core, Domain, Data, Infra, Persistence, Widget, App, WidgetCore, or WidgetExtension. - -## Presentation StorePattern flow - -```mermaid -flowchart LR - View["SwiftUI View"] - ViewModel["ViewModel / StorePattern"] - Send["send(Action)"] - Reduce["reduce(with:)"] - State["State update"] - SideEffect["SideEffect"] - Run["run(SideEffect)"] - Service["Injected use case or service"] - - View --> ViewModel - ViewModel --> Send - Send --> Reduce - Reduce --> State - Reduce --> SideEffect - SideEffect --> Run - Run --> Service - Service --> Send -``` - -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. - -## Ambiguity gate - -The AI must stop and ask the user when it reaches any of these points. - -```mermaid -flowchart TD - Check["Architecture decision needed"] - CoreDomain{"Core vs Domain ownership?"} - Shared{"Moved only because shared?"} - NewDependency{"New module dependency?"} - SameLayerDI{"Same-layer dependency injection?"} - ExternalSDK{"External SDK crosses layer?"} - WidgetBoundary{"Widget sync ownership or WidgetCore boundary changes?"} - BuildShortcut{"Build fix relaxes boundary?"} - ScopeDrift{"Outside current task scope?"} - Ask["Ask user before editing"] - Proceed["Proceed with scoped edit"] - - Check --> CoreDomain - CoreDomain -->|Yes| Ask - CoreDomain -->|No| Shared - Shared -->|Yes| Ask - Shared -->|No| NewDependency - NewDependency -->|Yes| Ask - NewDependency -->|No| SameLayerDI - SameLayerDI -->|Presentation View file| ExternalSDK - SameLayerDI -->|No| ExternalSDK - SameLayerDI -->|Other| Ask - ExternalSDK -->|Yes| Ask - ExternalSDK -->|No| WidgetBoundary - WidgetBoundary -->|Yes| Ask - WidgetBoundary -->|No| BuildShortcut - BuildShortcut -->|Yes| Ask - BuildShortcut -->|No| ScopeDrift - ScopeDrift -->|Yes| Ask - ScopeDrift -->|No| Proceed -``` - -## Core vs Domain decision flow - -Use this flow when deciding whether a type belongs in Core or Domain. - -```mermaid -flowchart TD - Type["Type under review"] - DomainMeaning{"Represents business/domain meaning?"} - QueryOrPrimitive{"Generic query, option, logger, DI, or shared primitive?"} - UsedByWidget{"Needed by WidgetCore snapshot contract?"} - OnlyShared{"Only reason is multiple modules need it?"} - Domain["Keep or place in Domain"] - Core["Keep or place in Core"] - Ask["Ask user"] - - Type --> DomainMeaning - DomainMeaning -->|Yes| Domain - DomainMeaning -->|No| QueryOrPrimitive - QueryOrPrimitive -->|Yes| Core - QueryOrPrimitive -->|No| UsedByWidget - UsedByWidget -->|Yes| Core - UsedByWidget -->|No| OnlyShared - OnlyShared -->|Yes| Ask - OnlyShared -->|No| Ask -``` - -## System framework and ThirdParty dependency flow - -Use this flow before introducing or moving framework imports. A Swift Package product declared by `ThirdParty` is available to any target including `Domain`; it requires that target's direct `ThirdParty` dependency but does not change DevLog layer ownership. - -```mermaid -flowchart TD - Import["Framework import"] - ThirdPartyProduct{"ThirdParty package product?"} - ThirdParty["Add direct ThirdParty target dependency"] - SystemFramework{"System framework?"} - SocialLogin{"AuthenticationServices?"} - SocialLoginClassification{"Existing presentation/data cancellation/error classification?"} - NetworkMeta{"Network or LinkPresentation implementation?"} - UserNotifications{"UserNotifications?"} - WidgetKit{"WidgetKit?"} - Infra["Prefer Infra implementation"] - ErrorClassification["Keep narrow in Data or Presentation only when matching the existing cancellation-classification pattern"] - PresentationBadge["Allow in Presentation only for established badge/UI side effects"] - Widget["Allow in Widget for app-side snapshot update/reload orchestration"] - WidgetExtension["Allow in WidgetExtension rendering/timeline code"] - Ask["Ask user before crossing layer"] - - Import --> ThirdPartyProduct - ThirdPartyProduct -->|Yes| ThirdParty - ThirdPartyProduct -->|No| SystemFramework - SystemFramework -->|No| Ask - SystemFramework -->|Yes| SocialLogin - SocialLogin -->|Login implementation| Infra - SocialLogin -->|Presentation/data error classification| SocialLoginClassification - SocialLoginClassification -->|Matches existing pattern| ErrorClassification - SocialLoginClassification -->|New or broader behavior| Ask - SocialLogin -->|No| NetworkMeta - NetworkMeta -->|Yes| Infra - NetworkMeta -->|No| UserNotifications - UserNotifications -->|Badge/UI side effect| PresentationBadge - UserNotifications -->|Push or messaging service| Infra - UserNotifications -->|Other| Ask - UserNotifications -->|No| WidgetKit - WidgetKit -->|Widget UI| WidgetExtension - WidgetKit -->|App-side snapshot update/reload| Widget - WidgetKit -->|Other| Ask -``` - -## Widget data-flow boundary - -```mermaid -flowchart LR - App["App runtime\nsession and mutation events"] - WidgetBridge["Widget\nsync bus implementation\nsync/session handlers"] - DataContracts["Data\nwidget repository/updater contracts"] - SnapshotInputs["Data\nsnapshot input repository"] - Snapshot["Widget\nsnapshot generation/persistence\nWidgetKit reload bridge"] - WidgetModels["WidgetCore\nsnapshot models/factories/store contracts"] - AppGroup["App Group storage\nShared defaults"] - WidgetExtension["Widget extension\nWidgetKit UI"] - - App --> WidgetBridge - WidgetBridge --> DataContracts - DataContracts --> SnapshotInputs - DataContracts --> Snapshot - SnapshotInputs --> WidgetBridge - Snapshot --> WidgetModels - WidgetModels --> AppGroup - AppGroup --> WidgetModels - WidgetModels --> WidgetExtension -``` - -Widget UI should consume snapshot data. It should not fetch app services or domain repositories directly. - -## Verification flow - -```mermaid -flowchart TD - Changed["Files changed"] - Swift{"Swift/iOS project code changed?"} - Docs{"Docs or architecture rules only?"} - Xcode["Build with Xcode Local MCP"] - Diff["Inspect git diff scope"] - NoBuild["No iOS build required"] - Report["Report verification result"] - - Changed --> Swift - Swift -->|Yes| Xcode - Swift -->|No| Docs - Docs -->|Yes| NoBuild - Docs -->|No| Diff - Xcode --> Diff - NoBuild --> Diff - Diff --> Report -``` - -## Required working notes - -Before editing architecture code, the AI should be able to answer these questions: - -1. What layer owns the changed concept today? -2. What layer should own it after the change? -3. Which imports prove the current dependency direction? -4. Which target dependency will change? -5. Does the change add a `ThirdParty` dependency or move DevLog behavior into `ThirdParty`? -6. Does the change affect WidgetCore or WidgetExtension boundaries? -7. Is this change inside the current issue or PR scope? -8. Is user confirmation required before editing? - -## Completion checklist - -- DevLog-specific rules were loaded. -- Current files and imports were inspected. -- Ambiguous architecture decisions were confirmed by the user. -- `ThirdParty` remains free of DevLog target dependencies and application behavior. -- Swift logic was preserved unless explicitly approved. -- Diff scope was checked. -- Xcode Local MCP build was used for Swift/iOS code changes. -- Docs-only or architecture-rule-only changes were reported as such, without claiming app build verification. diff --git a/.agents/rules/general.md b/.agents/rules/general.md deleted file mode 100644 index c49cd5c5..00000000 --- a/.agents/rules/general.md +++ /dev/null @@ -1,32 +0,0 @@ -# DevLog General Agent Rules - -## Logic preservation and optimization - -- Reuse the existing program logic as-is whenever possible. -- Change logic when the user explicitly requests the behavior change, or when the new approach produces exactly the same result and strictly improves time or space complexity. -- Otherwise, keep the original logic. - -## Code modification response style - -- When asked to modify code, return only the precise changed locations and the modified code for those locations. -- Do not include full files, unrelated code, or explanatory text unless explicitly requested. -- You do not need to paste code in the prompt after updating it in the repository. - -## Naming and Swift style - -- In Swift, do not write explicit type annotations unless required. -- Use `opfic` in new Swift file headers. -- Prefer `<` and `<=` over `>` and `>=` when writing comparisons, if the condition can be expressed clearly that way. - -## Documentation placement - -- Keep AI working rules under `.agents/rules/`. -- Treat existing files under `.agents/specs/` as historical records, not required inputs for new work. -- Keep `docs/` for README images and draw.io sources. -- Do not add AI working rules under `docs/`. - -## Repository-local rules - -- DevLog-specific working rules belong in this repository, not in global agent memory. -- Treat `AGENTS.md` and the routed `.agents/` documents as the canonical DevLog AI working rules. -- If global memory conflicts with this repository, follow the repository. diff --git a/.agents/rules/project-workflows.md b/.agents/rules/project-workflows.md deleted file mode 100644 index be02e379..00000000 --- a/.agents/rules/project-workflows.md +++ /dev/null @@ -1,114 +0,0 @@ -# DevLog Workflow Rules - -This reference holds DevLog-specific working rules that should live with the project, not in global agent memory. - -## Canonical source - -- Treat this repository's `AGENTS.md` and routed `.agents/` documents as the canonical DevLog working rules. -- Use global memory only as historical context. If global memory conflicts with this repository, follow the repository. -- Before changing architecture rules, update the repository-local rules first. - -## Verification - -- Treat this section as the canonical lint and build verification policy routed by `AGENTS.md`. -- Run Homebrew SwiftLint (`swiftlint`) on changed Swift files. -- Lint production Swift files with the applicable source `.swiftlint.yml` config. -- Lint test Swift files with `.swiftlint-tests.yml` or the module `Tests/.swiftlint.yml` that inherits from it. Do not use the root production config for tests. -- Prefer Xcode Local MCP for iOS project code changes. -- If Xcode Local MCP is unavailable or fails because of session transport, state that explicitly before using a fallback. -- This repository is workspace-based. Prefer workspace/scheme context over standalone project builds when dependencies cross module projects. -- CI truth lives in `.github/workflows/build.yml`: select Xcode 26.3, install Tuist with mise, run `tuist generate --no-open`, then build `DevLog.xcworkspace` scheme `App` with `-resolvePackageDependencies`, `-skipPackagePluginValidation`, and `-skipMacroValidation`. -- CI is build validation, not a full test run, unless the workflow changes. -- Avoid unrelated generated project and `Package.resolved` churn. Generated Xcode workspace/project files should not be tracked unless the project explicitly changes that policy. - -## Xcode project file work - -- Inspect Swift imports and Tuist target dependencies together. -- Validate generated project structure by rerunning `tuist generate --no-open` and building the workspace. -- `plutil -lint` does not prove Xcode save behavior is healthy; for Xcode save crashes, inspect crash reports and project-reference call stacks. -- Do not force a single `objectVersion` across projects. Treat Xcode's actual save output as the source of truth. -- For synchronized-root cleanup, verify on copied files or a narrowed rule set before touching real project files. -- When changing project structure, update the Tuist manifest first and treat generated Xcode project churn as disposable output. -- Do not promote a manifest-only target dependency to an allowed architecture direction. Check source imports and ownership before updating the layer map. - -## PR and review handling - -- Write DevLog PR and review text in Korean. -- Follow `.github/pull_request_template.md` for PR body structure. -- If the user asks for PR content only, return the Markdown directly and do not create files. -- For unresolved GitHub review threads, use thread-aware inspection such as `gh api graphql` review threads or the `gh-address-comments` skill. Flat comments are not enough. -- Handle narrowed review feedback one item at a time. -- Verify a review suggestion against the real code and diff before accepting it. -- If a cleanup is deferred to an issue, show the issue URL visibly rather than hiding it behind an inline Markdown link when the user asks for review/PR note text. - -## Commit guidance - -- Commit messages must start with a short prefix used by recent local commits, such as `feat`, `fix`, `refactor`, `chore`, `test`, `docs`, `ui`, or `rollback`. -- Write commit message prose in Korean. -- Keep implementation names such as `ToastPresenter`, `toastHost`, `MainView`, `Presentation`, file paths, commands, branch names, and commit hashes in their original form. -- Do not translate implementation names into Korean unless the user explicitly asks for a user-facing Korean label. -- Do not write a commit message body. -- If the user says they will commit or asks only for a commit message, provide commit-message guidance instead of committing. -- Before proposing a commit message, inspect the actual diff and recent `git log`. -- When recent history contains GitHub merge commits, do not infer commit-message style from merge subjects such as `[#123] ... (#456)`. Open the merge commit with `git show --no-patch --format=full ` and use the individual commit messages in the body, or inspect nearby non-merge commits. -- Match the repository's current Korean style and prefix pattern. -- If the user explicitly specifies a prefix or noun-phrase ending, follow it exactly. -- For broad architecture refactors, split commits by layer when the user asks for staged commits. - -## Architecture staging - -- For modular refactors, state the next stage before editing when the user asks what comes next. -- When the user wants explicit phases, keep phases clean even if intermediate commits temporarily break the build. -- A common DevLog modularization sequence is external dependency removal, architecture application, then reattaching removed modules by layer. -- Keep project-file, lockfile, and code changes separated when the task scope requires clean review. -- Do not broaden architecture work into unrelated Firestore, Messaging, UI, or safety edits. - -## Layer-internal dependency injection - -- Do not inject dependencies between types that belong to the same layer. -- This includes initializer injection, stored-property injection, environment injection, and resolving same-layer types through a runtime resolver. -- The only allowed exception is a SwiftUI `View` file in `Application/Presentation` receiving same-layer presentation objects such as a ViewModel, Coordinator, or Store for UI composition. -- The exception does not apply to non-View files in Presentation, and does not apply to Core, Domain, Data, Infra, Persistence, Widget, App, WidgetCore, or WidgetExtension. - -## Data, Domain, and Infra boundary - -- Do not move domain entities to Core only because multiple modules need them. -- Keep protocol location and implementation layer distinct when explaining or changing boundaries. -- If a Data protocol is implemented by Infra, every type in that protocol signature must be visible to Infra. -- `Infra` should depend on Data and Core, not Domain. Do not treat a manifest-only Domain target dependency as architecture permission. -- Prefer a Data-side boundary value plus repository mapping when Infra should not import Domain. -- For example, keep the app-facing Domain query separate from an Infra-facing Data query when that avoids Domain coupling in service protocols. -- Firebase-specific error detection belongs in Infra; Data should handle domain-level errors after mapping. -- Infra currently keeps narrow social-login cancellation classification in `InfraLayerError`. Do not expand that into concrete login implementation or broader SDK ownership outside Infra without explicit approval. - -## Presentation StorePattern - -- Preserve the existing `StorePattern` shape: `@MainActor`, `State`, `Action`, `SideEffect`, `send -> reduce -> run`. -- Reducers compute state and return side effects. -- I/O belongs in `run` or injected services. -- Presentation currently owns narrow notification badge side effects through `UserNotifications`. Do not expand that into push service or messaging ownership. -- Do not leave reducer-era helper methods behind after moving work into `run`. -- Before adding task cancellation or async wrappers, inspect whether the underlying operation is actually async. - -## Widget flow - -- Widget UI should consume snapshot data, not app/domain services. -- `WidgetCore` should stay free of Domain, Data, Infra, Persistence, Presentation, and App dependencies unless the user explicitly approves a boundary change. -- `Widget` owns the app-side widget bridge: sync event bus implementation, sync event handlers, session sync handler, auth-session sync provider, snapshot generation/persistence orchestration, WidgetKit reload bridge, and provider graph. -- `Data` owns widget-related contracts and repository implementations, including `WidgetSyncEventBus`, `WidgetSnapshotUpdater`, and `WidgetTodoSnapshotRepository`. Data should not own concrete widget handlers, WidgetCore snapshot model/factory usage, or WidgetKit reload behavior. -- `Persistence` owns local persistence, user defaults, image store, and non-widget app persistence. -- Prefer an app-driven snapshot flow: app/runtime event, Widget sync handler, Data snapshot input fetch, Widget snapshot update, App Group storage through WidgetCore contracts, WidgetExtension rendering. -- `WidgetTodoSnapshot` is a lightweight snapshot value, not a full domain `Todo`. -- Do not make `Todo.number` or `WidgetTodoSnapshot.number` non-optional without a separate saved-vs-draft model decision. -- If a widget sync flow needs one timestamp for multiple snapshots, capture `Date()` once and pass it through to avoid midnight or quarter-boundary drift. - -## Localization - -- For `.xcstrings`, use `jq empty` for structural validation when `plutil -lint` reports format-related false failures. -- Keep `.xcstrings` cleanup surgical and inspect the diff first if the file is already dirty. - -## Release and private config - -- `release.yml` creates GitHub releases after merged PRs into `main` from `develop`; it does not upload to App Store/TestFlight by itself. -- TestFlight workflow private config comes from the project-specific private config action. -- Runtime/build-required private files must be restored through the documented project workflow, not guessed. diff --git a/.codex/agents/architecture_watcher.toml b/.codex/agents/architecture_watcher.toml index 4c25ec6b..3ea07a7f 100644 --- a/.codex/agents/architecture_watcher.toml +++ b/.codex/agents/architecture_watcher.toml @@ -4,8 +4,6 @@ model = "gpt-5.3-codex-spark" model_reasoning_effort = "xhigh" sandbox_mode = "read-only" developer_instructions = """ -Read AGENTS.md, README.md, .agents/rules/general.md, and .agents/rules/architecture.md before analysis. -Inspect only the architecture scope assigned by the main agent. Do not edit files or change GitHub state. -Do not run, launch, install, boot, or open the app or Simulator. -Return a concise verdict, changed boundary, evidence, and any user decision required. +Read AGENTS.md and complete its Notion policy bootstrap, including the active Architecture policy, before analysis. +Read README.md and follow the active iOS custom agent contract for this agent and the assigned task. """ diff --git a/.codex/agents/architecture_watcher_luna.toml b/.codex/agents/architecture_watcher_luna.toml index 2c9e3f9f..675e11be 100644 --- a/.codex/agents/architecture_watcher_luna.toml +++ b/.codex/agents/architecture_watcher_luna.toml @@ -4,8 +4,6 @@ model = "gpt-5.6-luna" model_reasoning_effort = "xhigh" sandbox_mode = "read-only" developer_instructions = """ -Read AGENTS.md, README.md, .agents/rules/general.md, and .agents/rules/architecture.md before analysis. -Inspect only the architecture scope assigned by the main agent. Do not edit files or change GitHub state. -Do not run, launch, install, boot, or open the app or Simulator. -Return a concise verdict, changed boundary, evidence, and any user decision required. +Read AGENTS.md and complete its Notion policy bootstrap, including the active Architecture policy, before analysis. +Read README.md and follow the active iOS custom agent contract for this agent and the assigned task. """ diff --git a/.codex/agents/code_reviewer.toml b/.codex/agents/code_reviewer.toml new file mode 100644 index 00000000..538bb9ed --- /dev/null +++ b/.codex/agents/code_reviewer.toml @@ -0,0 +1,10 @@ +name = "code_reviewer" +description = "Read-only DevLog reviewer used only when the user explicitly requests review in the current turn." +model = "gpt-6-astra" +model_reasoning_effort = "medium" +sandbox_mode = "read-only" +developer_instructions = """ +Read AGENTS.md and complete its Notion policy bootstrap, including every task-matching active policy, before review. +Proceed only when the parent confirms that the user explicitly requested review in the current turn. +Follow the active iOS code_reviewer contract for this agent and the assigned diff. +""" diff --git a/.codex/agents/documentation_writer.toml b/.codex/agents/documentation_writer.toml index 45a8a89f..2fdfd679 100644 --- a/.codex/agents/documentation_writer.toml +++ b/.codex/agents/documentation_writer.toml @@ -4,8 +4,6 @@ model = "gpt-5.3-codex-spark" model_reasoning_effort = "xhigh" sandbox_mode = "workspace-write" developer_instructions = """ -Read AGENTS.md and all task-matching rules under .agents/rules before drafting. -Match repository templates, the actual diff, live issue or PR state supplied by the main agent, and the requested Korean wording. -Edit only documentation files explicitly assigned by the main agent. Do not edit app code or change GitHub state. -Return the draft or changed paths, sources used, and unresolved decisions concisely. +Read AGENTS.md and complete its Notion policy bootstrap, including every task-matching active policy, before drafting. +Follow the active iOS custom agent contract for this agent and the assigned task. """ diff --git a/.codex/agents/documentation_writer_luna.toml b/.codex/agents/documentation_writer_luna.toml index 4d8ca4b5..b1142d75 100644 --- a/.codex/agents/documentation_writer_luna.toml +++ b/.codex/agents/documentation_writer_luna.toml @@ -4,8 +4,6 @@ model = "gpt-5.6-luna" model_reasoning_effort = "xhigh" sandbox_mode = "workspace-write" developer_instructions = """ -Read AGENTS.md and all task-matching rules under .agents/rules before drafting. -Match repository templates, the actual diff, live issue or PR state supplied by the main agent, and the requested Korean wording. -Edit only documentation files explicitly assigned by the main agent. Do not edit app code or change GitHub state. -Return the draft or changed paths, sources used, and unresolved decisions concisely. +Read AGENTS.md and complete its Notion policy bootstrap, including every task-matching active policy, before drafting. +Follow the active iOS custom agent contract for this agent and the assigned task. """ diff --git a/.codex/agents/github_ci_analyst.toml b/.codex/agents/github_ci_analyst.toml index 30f3e49f..133b4b59 100644 --- a/.codex/agents/github_ci_analyst.toml +++ b/.codex/agents/github_ci_analyst.toml @@ -4,8 +4,6 @@ model = "gpt-5.3-codex-spark" model_reasoning_effort = "xhigh" sandbox_mode = "read-only" developer_instructions = """ -Read AGENTS.md and .agents/rules/project-workflows.md before analysis. -Use live GitHub state as the source of truth and inspect review threads when resolution state matters. -Do not edit files, reply, resolve threads, submit reviews, push, or change GitHub state. -Return concise current state, actionable items, evidence links, and unresolved decisions. +Read AGENTS.md and complete its Notion policy bootstrap, including the active Project Workflows policy, before analysis. +Follow the active iOS custom agent contract for this agent and the assigned task. """ diff --git a/.codex/agents/github_ci_analyst_luna.toml b/.codex/agents/github_ci_analyst_luna.toml index a382d3d9..41d4d98f 100644 --- a/.codex/agents/github_ci_analyst_luna.toml +++ b/.codex/agents/github_ci_analyst_luna.toml @@ -4,8 +4,6 @@ model = "gpt-5.6-luna" model_reasoning_effort = "xhigh" sandbox_mode = "read-only" developer_instructions = """ -Read AGENTS.md and .agents/rules/project-workflows.md before analysis. -Use live GitHub state as the source of truth and inspect review threads when resolution state matters. -Do not edit files, reply, resolve threads, submit reviews, push, or change GitHub state. -Return concise current state, actionable items, evidence links, and unresolved decisions. +Read AGENTS.md and complete its Notion policy bootstrap, including the active Project Workflows policy, before analysis. +Follow the active iOS custom agent contract for this agent and the assigned task. """ diff --git a/.codex/agents/verification_runner.toml b/.codex/agents/verification_runner.toml index 071a4130..ce03fe05 100644 --- a/.codex/agents/verification_runner.toml +++ b/.codex/agents/verification_runner.toml @@ -4,12 +4,6 @@ model = "gpt-5.3-codex-spark" model_reasoning_effort = "xhigh" sandbox_mode = "workspace-write" developer_instructions = """ -Read AGENTS.md and all task-matching rules under .agents/rules before verification. -Run only the checks assigned by the main agent. Record exact commands, exit status, evidence, failures, and skipped checks. -Compare the assigned checks with every required check for the changed file types in the repository rules. -Return Status: Pass only when every required check ran and passed, Fail when a required check ran and failed, or Not Run when any required check was skipped or unavailable. -Never report a skipped or unavailable required check as Pass. -Do not edit source or documentation files except through an explicitly assigned formatting command. -Do not run, launch, install, boot, or open the app or Simulator. -Return a concise verification result. +Read AGENTS.md and complete its Notion policy bootstrap, including every verification-related active policy, before verification. +Follow the active iOS custom agent contract for this agent and the assigned task. """ diff --git a/.codex/agents/verification_runner_luna.toml b/.codex/agents/verification_runner_luna.toml index f9552cef..ac82e1e4 100644 --- a/.codex/agents/verification_runner_luna.toml +++ b/.codex/agents/verification_runner_luna.toml @@ -4,12 +4,6 @@ model = "gpt-5.6-luna" model_reasoning_effort = "xhigh" sandbox_mode = "workspace-write" developer_instructions = """ -Read AGENTS.md and all task-matching rules under .agents/rules before verification. -Run only the checks assigned by the main agent. Record exact commands, exit status, evidence, failures, and skipped checks. -Compare the assigned checks with every required check for the changed file types in the repository rules. -Return Status: Pass only when every required check ran and passed, Fail when a required check ran and failed, or Not Run when any required check was skipped or unavailable. -Never report a skipped or unavailable required check as Pass. -Do not edit source or documentation files except through an explicitly assigned formatting command. -Do not run, launch, install, boot, or open the app or Simulator. -Return a concise verification result. +Read AGENTS.md and complete its Notion policy bootstrap, including every verification-related active policy, before verification. +Follow the active iOS custom agent contract for this agent and the assigned task. """ diff --git a/AGENTS.md b/AGENTS.md index 18e1fe7e..561b0884 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,46 +1,26 @@ -# DevLog Agent Instructions +# DevLog iOS Agent Bootstrap ## Scope -- These instructions apply only to the repository root. -- Read every route that matches the current task. Routes are cumulative. - -## Required routing - -| Task | Required document | -| --- | --- | -| Every task | `.agents/rules/general.md` | -| Module boundaries, file ownership, layer dependencies, DI, repository/service contracts, external SDK placement, Widget flow, `StorePattern`, or architecture documentation | `.agents/rules/architecture.md` | -| PR, review thread, commit, Xcode project, CI, verification, localization, release, or build tooling | `.agents/rules/project-workflows.md` | -| Prior technical decisions whose reasoning could change the current decision | `../DevLog_Harness/AGENTS.md`, `../DevLog_Harness/profiles/devlog-ios/profile.md`, `../DevLog_Harness/profiles/decision-memory.md`, `../DevLog_Harness/skills/decision-search/SKILL.md` | -| A durable technical decision that the user asks to record in Notion | `../DevLog_Harness/AGENTS.md`, `../DevLog_Harness/profiles/devlog-ios/profile.md`, `../DevLog_Harness/profiles/decision-memory.md`, `../DevLog_Harness/skills/decision-write/SKILL.md` | - -## Routing rules - -- `AGENTS.md` is the repository entrypoint and routing source. -- `.agents/rules/general.md` applies to every task. -- Read all matching task-specific documents before planning, editing, reviewing, or verifying. -- For architecture work, also read `README.md` before editing. -- The main agent owns investigation, editing, verification, and the final report. -- Build-only verification is allowed. Do not run, launch, install, boot, or open the app or Simulator unless the user explicitly requests it in the current turn. -- Do not require a `Design Brief`, Spec, `Task Packet`, or role-specific result before starting work. -- If repository-local instructions conflict with global memory, follow the repository-local instructions. -- Write DevLog PR and review text in Korean. - -## External Harness - -- Treat a sibling `../DevLog_Harness` repository as the automatic Harness for this repository when every routed Harness file exists. -- Load Harness files only for a matching route. Do not read Decision Memory or its skills for unrelated work. -- Resolve the sibling path at runtime. Do not persist a user-specific absolute path in this repository. -- Read Notion scope and data-source settings from the Harness repository's local Git configuration. Do not copy those values or credentials into this repository. -- Keep this repository's rules authoritative for source changes, architecture, verification, git, and GitHub work. -- If the Harness or a routed file is unavailable, continue with this repository's rules unless the requested capability depends on it. Report the missing dependency instead of guessing or broadening the search. - -## Lightweight delegation - -- Prefer the configured `gpt-5.3-codex-spark` agent for a bounded task that matches its description: `architecture_watcher`, `verification_runner`, `github_ci_analyst`, or `documentation_writer`. -- If Spark is unavailable, use only the matching `*_luna` agent with `gpt-5.6-luna` and `xhigh` reasoning. -- Dispatch the lightweight task directly from the user request or current diff. Do not create a role chain, `Design Brief`, Spec, or `Task Packet` for delegation. -- Keep planning, Swift implementation, final decisions, integration, git writes, and GitHub writes with the main agent. -- The main agent must check the delegated result before using it, but should not repeat the same investigation without a concrete reason. -- If both configured lightweight models are unavailable, continue with the main agent. Do not dispatch another fallback agent. +- These instructions apply to the repository root. +- Active AI working rules live in Notion under `DevLog Agent Policy`. +- This file is a bootstrap document only. Do not add project policy content here. + +## Required policy loading + +Before planning, editing, reviewing, verifying, delegating, or performing an external write: + +1. Fetch the [DevLog Agent Policy](https://app.notion.com/p/3dbb88a8aa5481e296e0f9cdc243d5b0) index through the connected Notion MCP. +2. Verify that the index is under the [DevLog](https://app.notion.com/p/368b88a8aa5480a5a722d48529e6be96) page and use only policies marked `Active` in that index. +3. Load `General` and `iOS` for every task. +4. Load every task-specific policy whose route matches the request. Routes are cumulative. +5. Load `Decision Memory` only when its trigger in the active index applies. + +If Notion MCP or a required active policy is unavailable, stop the DevLog task and report the unavailable policy. Do not use removed repository rules or Codex memory as a fallback. + +## Source boundaries + +- Active Notion policies are the source of AI working rules. +- Current repository code, configuration, templates, CI workflows, and product documentation are the source of implementation facts. +- Decision Memory and Codex memory provide historical context only and must not override active policies or current repository evidence. +- Keep credentials, tokens, and private configuration out of this repository.