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
22 changes: 22 additions & 0 deletions .analysis/outputs/SUMMARY.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"session_id": "analyze-20260712-130508",
"mode": "analyze",
"scope": ".",
"status": "completed",
"commit": "1020096a14b65c7bd21ca081947f1747978e3b93",
"visited_count": 51,
"frontier_count": 0,
"modules": [],
"outputs": [
"full-audit.md",
"overview.md",
"architecture.md",
"technologies.md",
"glossary.md",
"module-map.json",
"dependency-graph.json",
"issue-candidates.md",
"implementation-checklist.md"
],
"checkpoints_count": 2
}
17 changes: 17 additions & 0 deletions .analysis/outputs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Linetta 아키텍처

```text
React UI → TypeScript rpc.ts → Tauri command → Rust FFI → Go C ABI
→ engineapp/rpc handlers → domain repositories → SQLite
```

주요 경계:

- UI/RPC: `apps/desktop/src/lib/rpc.ts`
- Tauri: `apps/desktop/src-tauri/src/lib.rs`
- FFI: `apps/desktop/src-tauri/src/ffi.rs`, `engine/cmd/linetta-ffi/ffi.go`
- composition root: `engine/internal/engineapp/engineapp.go`
- persistence: `engine/internal/store`, domain repositories
- recovery/export: `engine/internal/backup`, `export`, `importmd`, `foldersync`, `gitsync`

상세 그래프와 이슈는 `full-audit.md`를 참조한다.
14 changes: 14 additions & 0 deletions .analysis/outputs/dependency-graph.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"apps/desktop/src/routes/Workspace.tsx":["apps/desktop/src/lib/rpc.ts","apps/desktop/src/components/editor/Tiptap.tsx","apps/desktop/src/hooks/useDebouncedCallback.ts"],
"apps/desktop/src/lib/rpc.ts":["apps/desktop/src-tauri/src/lib.rs"],
"apps/desktop/src-tauri/src/lib.rs":["apps/desktop/src-tauri/src/ffi.rs","apps/desktop/src-tauri/src/folder_sync.rs"],
"apps/desktop/src-tauri/src/ffi.rs":["engine/cmd/linetta-ffi/ffi.go"],
"engine/cmd/linetta-ffi/ffi.go":["engine/internal/engineapp/engineapp.go"],
"engine/internal/engineapp/engineapp.go":["engine/internal/rpc/server.go","engine/internal/rpc/handlers","engine/internal/store/store.go","engine/internal/backup","engine/internal/companion"],
"engine/internal/rpc/handlers":["engine/internal/project","engine/internal/node","engine/internal/entity","engine/internal/thread","engine/internal/export","engine/internal/importmd"],
"engine/internal/project":["engine/internal/store/store.go"],
"engine/internal/node":["engine/internal/store/store.go","engine/internal/manuscript","engine/internal/mention"],
"engine/internal/backup":["engine/internal/store/store.go"],
"engine/internal/foldersync":["engine/internal/export"],
"engine/internal/gitsync":["engine/internal/export"]
}
500 changes: 500 additions & 0 deletions .analysis/outputs/full-audit.md

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions .analysis/outputs/glossary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# 용어

- Engine: Go domain/runtime layer embedded through C ABI.
- FFI: Rust와 Go 사이 C-compatible function boundary.
- RPC: frontend method/params를 Go handler에 전달하는 JSON-RPC contract.
- Snapshot: scene content version history.
- Daily backup: SQLite `VACUUM INTO` copy.
- Folder/Git sync: Markdown export copies; database-complete backup이 아님.
- Fact Book: source-backed story facts UI.
- Companion: AI writing assistant flow.
29 changes: 29 additions & 0 deletions .analysis/outputs/implementation-checklist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# 감사 후 구현 체크리스트

## Release blockers

- [x] stale autosave cross-scene regression test를 먼저 추가
- [x] node/version-bound save, cancel/flush, optimistic lock 구현
- [x] daily backup 실패 후 당일 retry, SQLite quick_check, cancel-aware scheduler 구현
- [x] 앱 안 privacy policy 링크와 third-party AI 명시 동의 추가
- [x] Go/React Router/Rust patch update 후 세 dependency audit 재실행

## This month

- [x] sync manifest/atomic write/partial failure reporting
- [x] Markdown “backup” 명칭 정정과 versioned `.linetta` recovery format
- [x] startup recovery/pre-migration backup/migration checksum
- [x] atomic import cleanup, FTS/mention startup reconcile
- [x] IPC error envelope 및 i18n key/placeholder executable contract tests
- [x] Workspace persistence queue, typed app events, React Hooks lint gate
- [ ] Tiptap walker/OpenRouter controller 추출 — 기능 변경과 맞닿을 때 수행할 비차단 리팩터링

## Verification (2026-07-14)

- [x] `make ci`
- [x] `go test -race ./...`
- [x] `cargo test --features mas`
- [x] `govulncheck ./...`, `pnpm audit --prod`, `cargo audit`
- [x] `make validate-distribution`, `git diff --check`

전체 완료 기준과 비권장 항목은 `full-audit.md`를 참조한다.
36 changes: 36 additions & 0 deletions .analysis/outputs/issue-candidates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Issue Candidates

### SEC-001: stale autosave cross-scene overwrite

- Module: `apps/desktop/src/routes/Workspace.tsx`
- Type: `SEC`
- Evidence: delayed doc argument is executed by latest callback capturing a different node ID.
- Suggested action: node/version-bound save queue, cancel/flush, optimistic locking, regression tests.

### TIDY-001: Workspace responsibility concentration

- Module: `apps/desktop/src/routes/Workspace.tsx`
- Type: `TIDY`
- Evidence: UI, outline, save ordering, AI orchestration and render tree live in one 2k-line component.
- Suggested action: after SEC-001 hotfix, extract test seams, outline and persistence hooks incrementally.

### DUP-001: repeated Tiptap tree walkers

- Module: `engine/internal`
- Type: `DUP`
- Evidence: five near-identical document-to-text walkers drift independently.
- Suggested action: share only identical output policy in `internal/tiptapdoc`.

### SEC-002: backup success marker precedes verified backup

- Module: `engine/internal/backup`
- Type: `SEC`
- Evidence: date directory is created before VACUUM and treated as completion.
- Suggested action: validated temp backup plus atomic completion marker.

### TIDY-002: RPC and localization contracts lack executable schemas

- Module: `apps/desktop/src/lib`
- Type: `TIDY`
- Evidence: wire null/optional drift and `MessageKey` begins with `string`.
- Suggested action: fixture contract tests and i18n catalog/placeholder tests.
10 changes: 10 additions & 0 deletions .analysis/outputs/module-map.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"frontend": {"path":"apps/desktop/src","responsibility":"React UI, editor state, localization, typed RPC wrappers","key_files":["apps/desktop/src/routes/Workspace.tsx","apps/desktop/src/lib/rpc.ts","apps/desktop/src/lib/types.ts","apps/desktop/src/lib/i18n.tsx"]},
"tauri_shell": {"path":"apps/desktop/src-tauri","responsibility":"Desktop security boundary, command exposure, Go FFI lifecycle","key_files":["apps/desktop/src-tauri/src/lib.rs","apps/desktop/src-tauri/src/ffi.rs","apps/desktop/src-tauri/capabilities/default.json","apps/desktop/src-tauri/tauri.conf.json"]},
"engine_composition": {"path":"engine/internal/engineapp","responsibility":"Construct services, register RPC methods, run background jobs","key_files":["engine/internal/engineapp/engineapp.go"]},
"rpc": {"path":"engine/internal/rpc","responsibility":"JSON-RPC dispatch and input/output adapters","key_files":["engine/internal/rpc/server.go","engine/internal/rpc/handlers"]},
"persistence": {"path":"engine/internal/store","responsibility":"SQLite connection, migrations, shared repositories","key_files":["engine/internal/store/store.go","engine/internal/store/migrations.go"]},
"domain": {"path":"engine/internal","responsibility":"Project, node, entity, thread, plot, fact, snapshot, AI and companion behavior","key_files":["engine/internal/node/repo.go","engine/internal/project/repo.go","engine/internal/companion/companion.go"]},
"recovery_export": {"path":"engine/internal","responsibility":"Backup, Markdown import/export, folder and Git sync","key_files":["engine/internal/backup/backup.go","engine/internal/export/project.go","engine/internal/importmd/builder.go","engine/internal/foldersync/foldersync.go","engine/internal/gitsync/gitsync.go"]},
"distribution": {"path":"scripts","responsibility":"Build, sign, notarize, MAS package and distribution validation","key_files":["scripts/release-mas-local.sh","scripts/release-macos-local.sh","scripts/validate-distribution.sh"]}
}
7 changes: 7 additions & 0 deletions .analysis/outputs/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Linetta 분석 개요

- 기준: `1020096a14b65c7bd21ca081947f1747978e3b93`
- 구조: React/Tauri Rust/Go FFI/SQLite 단일 로컬 앱
- Blocking: pending debounce가 최신 scene ID와 결합해 다른 씬을 덮어쓸 수 있음
- 배포 gate: 제3자 AI 전송의 앱 내 명시 고지/동의와 privacy policy 정합성
- 전체 보고서: `full-audit.md`
9 changes: 9 additions & 0 deletions .analysis/outputs/technologies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# 기술 구성

- Desktop shell: Tauri 2 / Rust 2021
- Frontend: React 18, TypeScript, Vite, Tiptap
- Engine: Go 1.26.2 at audited commit
- Persistence: SQLite via `modernc.org/sqlite`, WAL, single connection
- IPC: Tauri invoke + JSON-RPC envelope over in-process Go C ABI
- Distribution: Mac App Store sandbox, Developer ID/notarized direct macOS, Windows/Linux packaging
- Localization: ko baseline, en, ja; single TypeScript catalog
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,13 @@ jobs:
with:
workspaces: apps/desktop/src-tauri

- name: Install security scanners
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
cargo install cargo-audit --locked

- name: Verify
run: make ci

- name: Audit dependencies
run: make audit
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,9 @@ scripts/ascapi/ascapi

# Serena tool workspace
.serena/

# Local audit working state (final reports live in .analysis/outputs)
/.analysis/cache/
/.analysis/sessions/
/.analysis/AI_CONTEXT.md
/.analysis/RESUME.md
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## v0.9.2 - 2026-07-14

- Prevented stale autosaves from overwriting another scene by serializing per-scene writes and enforcing optimistic content-version checks.
- Added verified daily and pre-migration backups, full-library `.linetta` recovery snapshots, startup restore controls, and atomic sync/import writes with partial-failure reporting.
- Required explicit, revocable consent before manuscript content is sent to a selected AI provider, and aligned the in-app disclosure and Korean/English/Japanese privacy policy.
- Narrowed renderer IPC and file-opening access, preserved structured RPC errors, aligned localization catalogs, and added lint and dependency-audit gates to CI.

## v0.9.1 - 2026-07-05

- Localized every AI surface for the selected app language (Korean/English/Japanese): the companion, inline editor AI, contextual edit, and Fact Book now build prompts and reply in the app language, including tool status text, apply results, and query outputs.
Expand Down
15 changes: 13 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: help dev test test-go test-desktop test-tauri test-mobile-engine validate-actions-runtime validate-distribution build-engine build-mobile-engine-ios build-mobile-engine-android mobile-ios-init mobile-android-init build-mobile-ios-sim smoke-mobile-ios-sim dev-mobile-ios build-mobile-android-debug build-mobile-android-release-smoke patch-mobile-android-signing build-desktop release-macos-local build-mas-local release-mas-local bump-version ci
.PHONY: help dev test test-go test-desktop test-tauri test-mobile-engine audit audit-go audit-desktop audit-rust validate-actions-runtime validate-distribution build-engine build-mobile-engine-ios build-mobile-engine-android mobile-ios-init mobile-android-init build-mobile-ios-sim smoke-mobile-ios-sim dev-mobile-ios build-mobile-android-debug build-mobile-android-release-smoke patch-mobile-android-signing build-desktop release-macos-local build-mas-local release-mas-local bump-version ci

help: ## Show available targets
@awk 'BEGIN {FS = ":.*##"} /^[a-zA-Z0-9_-]+:.*##/ {printf "%-18s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
Expand All @@ -10,11 +10,22 @@ test: test-go test-desktop test-tauri ## Run all local verification

ci: validate-actions-runtime test ## Run the CI verification contract

audit: audit-go audit-desktop audit-rust ## Check reachable and locked dependency vulnerabilities

audit-go: ## Check reachable Go vulnerabilities (requires govulncheck)
cd engine && govulncheck ./...

audit-desktop: ## Check production frontend dependencies
cd apps/desktop && pnpm audit --prod

audit-rust: ## Check RustSec advisories (requires cargo-audit)
cd apps/desktop/src-tauri && cargo audit

test-go: ## Run Go engine tests
cd engine && go test ./...

test-desktop: ## Run desktop frontend tests and production build
cd apps/desktop && pnpm test && pnpm build
cd apps/desktop && pnpm lint && pnpm test && pnpm build

test-tauri: ## Type-check the Tauri shell
cd apps/desktop/src-tauri && cargo check
Expand Down
24 changes: 24 additions & 0 deletions apps/desktop/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import reactHooks from "eslint-plugin-react-hooks";
import tseslint from "typescript-eslint";

export default tseslint.config({
ignores: ["dist/**", "src-tauri/**"],
files: ["src/**/*.{ts,tsx}"],
languageOptions: {
parser: tseslint.parser,
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
ecmaFeatures: { jsx: true },
},
},
plugins: {
"react-hooks": reactHooks,
"@typescript-eslint": tseslint.plugin,
},
rules: {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"@typescript-eslint/no-explicit-any": "warn",
},
});
8 changes: 6 additions & 2 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
{
"name": "linetta-desktop",
"version": "0.9.1",
"version": "0.9.2",
"license": "AGPL-3.0-only",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint src --max-warnings 28",
"test": "vitest run",
"preview": "vite preview",
"tauri": "tauri"
Expand All @@ -26,7 +27,7 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-markdown": "^10.1.0",
"react-router-dom": "^6.30.3",
"react-router-dom": "^6.30.4",
"remark-gfm": "^4.0.1"
},
"devDependencies": {
Expand All @@ -38,8 +39,11 @@
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
"eslint": "10.7.0",
"eslint-plugin-react-hooks": "7.1.1",
"jsdom": "^29.1.1",
"typescript": "^5.5.4",
"typescript-eslint": "8.63.0",
"vite": "^5.4.0",
"vitest": "^2.1.9"
}
Expand Down
Loading
Loading