Skip to content
Open
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
5 changes: 4 additions & 1 deletion .codex/skills/soundlog-codex-workflow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ description: Soundlog React Native/Expo 앱에서 기능을 설계하거나 구

Before changing code, load only the docs relevant to the task:

- Recap/Log/TravelSession canonical domain: `docs/product/RECAP_LOG_DOMAIN_MODEL.md`
- Product context: `docs/product/SOUNDLOG_APP_PLANNING.md`
- RN frontend principles: `docs/frontend/RN_FRONTEND_PLANNING_POINTS.md`
- Home screen: `docs/frontend/MAIN_PAGE_RN_BUILD_DOC.md`
Expand All @@ -24,7 +25,7 @@ Before changing code, load only the docs relevant to the task:
2. Define the feature contract: user goal, affected screens, state, API/mock needs, permissions, loading/empty/error/offline states.
3. Ask the user only about product-risk edge cases, especially privacy, location tracking, music-platform policy, persistence, or sharing behavior.
4. Create a concrete implementation plan with files to modify and verification steps.
5. Review the plan before editing. If Claude review is available, use it; otherwise perform a Codex self-review and revise the plan.
5. Review the plan before editing. Perform a Codex self-review and revise the plan when it exposes a product, data, privacy, or verification gap.
6. Implement with scoped changes.
7. Verify with `npm run typecheck`; for UI work also check web or simulator when practical.
8. Review the diff, fix issues, then summarize. Commit only when the user asks.
Expand All @@ -42,6 +43,8 @@ For natural-language UI feedback, use the narrower `soundlog-ui-feedback-loop` w
- Avoid top-level imports of native-only modules when web can load the file. Lazy import native modules or guard with `Platform.OS`.
- Prefer query-backed data for server/mock state and local stores only for user selections or lightweight persisted UI state.
- Recap UI should preserve visual share quality: stable capture frame, clear CTA states, graceful fallback when sharing or media permissions fail.
- A product Recap is one camera-flow capture. A product Log is the set of Recaps sharing one travel `sessionId`; standalone Recaps must not appear as Logs.
- A Log detail map must render only that Log's Recap pins and session route.

## Verification Checklist

Expand Down
22 changes: 22 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Soundlog Agent Rules

- Do not merge GitHub pull requests unless the user's latest message explicitly asks to merge that exact PR.
- PR creation, CI verification, reviewer assignment, issue closing, or "finish the task" does not imply merge approval.
- If a merge request is ambiguous, ask for confirmation before using any merge UI, GitHub API, or commands such as `gh pr merge`.

## Platform Direction

- Soundlog is a mobile app product. We do not intend to ship or maintain a public web deployment as a product surface.
- Treat Expo web as a development or CI/export compatibility target only. Do not design product behavior around web deployment unless the user explicitly asks for web support.
- When a feature depends on native capabilities such as camera, location, media library, sharing, secure storage, or app permissions, prioritize iOS/Android behavior and native Expo APIs.
- Do not block mobile feature work just because the same flow cannot fully work on web. Provide a minimal web fallback only when it is needed for local development, type checking, preview safety, or export stability.

## Recap And Log Domain

- Before changing Recap, Log, camera capture, travel mode, route tracking, map pins, visibility, or related API behavior, read `docs/product/RECAP_LOG_DOMAIN_MODEL.md`.
- Treat that document as the canonical product contract when older planning docs or legacy code names conflict with it.
- Product `Recap` means one capture saved from the camera flow.
- Product `Log` means one or more Recaps created in the same travel-mode session. A standalone Recap created outside travel mode is not a one-item Log.
- A travel Log is identified by `sessionId`, even when it contains only one Recap. Never merge Logs by date, place, distance, or track.
- A Log detail map may show only its member Recap pins and its own session route. Do not mix nearby/public pins or other Logs into that map.
- `MomentLog` and the server `Recap` model are legacy technical names. Follow the product-to-code mapping in the canonical domain document instead of exposing those terms to users.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,16 @@ npm run dev:client

```bash
npm run typecheck
npm run check:recap-clustering
npm run check:server-contract
npm run check
npm run web:clear
```

`check:recap-clustering`은 전체 리캡과 내 리캡 지도의 좌표 묶음, 줌별 반경, 확대 시 개별 핀 분리를 검증합니다. `check`에도 포함되어 있습니다.

`check:server-contract`은 형제 경로의 `SoundLogServer/openapi/soundlog-api.yaml`과 프론트 `src/api`의 HTTP 메서드·경로를 비교합니다. 서버 저장소 위치가 다르면 `SOUNDLOG_SERVER_ROOT`를 지정합니다.

레거시 mock API 상태를 확인하던 지연/실패 환경변수는 `src/mock-server` 참고용으로만 남아 있습니다. 현재 앱 API facade는 서버 API만 호출합니다.

```bash
Expand Down Expand Up @@ -79,6 +85,7 @@ iOS는 TestFlight 또는 ad hoc 기기 등록이 필요합니다. App Store/Test
## 문서

- [문서 인덱스](docs/README.md)
- [리캡·로그 도메인 기준](docs/product/RECAP_LOG_DOMAIN_MODEL.md)
- [서비스 기획서](docs/product/SOUNDLOG_APP_PLANNING.md)
- [React Native 프론트 고려사항](docs/frontend/RN_FRONTEND_PLANNING_POINTS.md)
- [비개발자용 Codex 개발 가이드](docs/codex/NON_DEVELOPER_CODEX_GUIDE.md)
Expand Down
27 changes: 27 additions & 0 deletions app.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ function getApiBaseUrl() {
return process.env.EXPO_PUBLIC_SOUNDLOG_API_BASE_URL?.replace(/\/+$/, '');
}

function getGoogleMapsAndroidApiKey() {
return (
process.env.EXPO_PUBLIC_GOOGLE_MAPS_ANDROID_API_KEY ??
process.env.GOOGLE_MAPS_ANDROID_API_KEY
);
}

function isProductionBuildProfile() {
return process.env.EAS_BUILD_PROFILE === 'production';
}
Expand Down Expand Up @@ -166,11 +173,31 @@ function upsertBuildPropertiesPlugin(config, nextAndroidConfig) {
);
}

function applyAndroidGoogleMapsApiKey(config) {
const apiKey = getGoogleMapsAndroidApiKey();

if (!apiKey) {
return;
}

config.android = {
...config.android,
config: {
...(config.android?.config ?? {}),
googleMaps: {
...(config.android?.config?.googleMaps ?? {}),
apiKey,
},
},
};
}

module.exports = () => {
const nextConfig = JSON.parse(JSON.stringify(baseConfig));
const apiBaseUrl = getApiBaseUrl();
const httpApiHost = getHttpApiHost(apiBaseUrl);

applyAndroidGoogleMapsApiKey(nextConfig);
assertProductionConfig(apiBaseUrl);

if (!httpApiHost || isProductionBuildProfile()) {
Expand Down
Loading
Loading