+ );
+}
+
+```## 📝 5. Content Creation (Writing)
+
+Frontmatter example:```yaml
+---
+title: "04.24 东京天空树"
+date: "2026-01-04"
+description: "世界上最暖和的地方在天空树的顶上。"
+tags: ["Diary", "Tokyo"]
+---
+
+```---
+
+## 🚀 6. Deploy Checklist
+
+1. Ensure `package.json` includes React 19 overrides.
+2. Verify that the color configuration in `tailwind.config.js` is correct.
+3. Confirm that all page components properly handle `await params`.
+4. The Sakura Easter‑egg component has been imported in `layout.jsx` or `page.jsx`.
+
+> **Ending:** “We are all little monsters, and one day we will be slain by the righteous Ultraman.” — But here, your words will be treasured forever like Er
\ No newline at end of file
diff --git a/translations/en/README.md b/translations/en/README.md
new file mode 100644
index 0000000..cc4034a
--- /dev/null
+++ b/translations/en/README.md
@@ -0,0 +1,84 @@
+# ERII Blog
+
+Personal blog project, currently using a **DB-only content architecture**:
+
+- Public content source: Postgres `posts(status='published')`
+- Writing entry point: `/write` (token‑based session authentication)
+- Media assets: Vercel Blob
+- Content format: dual mode with `mdx` + `blocks`
+- Sync capabilities: manual Notion sync + Webhook sync
+
+## Tech Stack
+
+- Next.js 16.1.1 (App Router)
+- React 19.2.3
+- Tailwind CSS 4
+- `next-mdx-remote` / `react-markdown`
+- `@vercel/postgres` / `@vercel/blob`
+
+## Quick Start
+
+1. Install```bash
+corepack enable
+pnpm install
+```2. Configure environment variables (locally recommended to place them in `.env.local`)
+
+- `DATABASE_URL` (or `POSTGRES_URL`)
+- `BLOB_READ_WRITE_TOKEN`
+- `ERII_WRITE_PASSWORD`
+- `ERII_WRITE_SESSION_SECRET`
+- Optional Notion:
+ - `NOTION_TOKEN`
+ - `NOTION_API_VERSION` (default `2022-06-28`)
+ - `ERII_NOTION_WEBHOOK_SECRET`
+
+3. Initialize the database (first time)
+
+- Run: `db/schema.sql`
+- Or run migration: `db/migrations/20260304_content_platform.sql`
+
+4. Start```bash
+pnpm dev
+```Default port: `http://localhost:3239`
+
+## Common Scripts
+
+- `pnpm dev`: local development
+- `pnpm lint`: ESLint checking
+- `pnpm build`: production build
+- `pnpm start`: start production service
+
+## Content and Writing Capabilities
+
+- `/write` supports:
+ - login/logout sessions
+ - draft saving
+ - publishing (triggers revalidate)
+ - article list retrieval and loading
+ - image/cover upload to Blob
+ - MDX / Blocks dual editing mode
+- Versioning capabilities:
+ - `post_revisions` snapshot records
+ - support restoration by version
+- Notion sync:
+ - Manual: `POST /api/write/sync/notion`
+ - Webhook: `POST /api/write/sync/notion/webhook`
+
+## Key Endpoints (Writing Side)
+
+- `GET/POST/DELETE /api/write/session`
+- `GET/POST /api/write/posts`
+- `GET /api/write/posts/[slug]`
+- `POST /api/write/posts/publish`
+- `GET/POST /api/write/posts/[slug]/revisions`
+- `POST /api/write/assets`
+- `POST /api/write/sync/notion`
+- `POST /api/write/sync/notion/webhook`
+- `GET /api/health`
+
+## Documentation
+
+- Documentation navigation: `docs/README.md`
+- Project status report: `docs/Project-Feature-and-Progress-Report.md`
+- Runbook: `docs/ContentPlatform-Runbook.md`
+- Content platform implementation plan: `docs/ContentPlatform-Implementation-Plan.md`
\ No newline at end of file
diff --git a/translations/en/VERCEL_PUBLISHING_TBD.md b/translations/en/VERCEL_PUBLISHING_TBD.md
new file mode 100644
index 0000000..18f47b7
--- /dev/null
+++ b/translations/en/VERCEL_PUBLISHING_TBD.md
@@ -0,0 +1,280 @@
+# ERII Blog: /write Direct Publishing to Vercel – Pending Plan
+
+> Goal: Publish the edited content from `/write` “directly and display it on the live site”.
+>
+> Status: **MVP implemented** (Postgres + Blob + draft/publish + password‑session authentication + public read path migration). This document retains the solution history and adds “current implementation and usage steps”.
+>
+> > Update: The current project has converged the public reading content source to a single **Postgres (`published`)** source, and has deprecated/removed the fallback reading logic for `content/*.mdx`; any references to `content/` below are for historical reference only.
+
+---
+
+## 0. Confirmed Choices (Can Serve as Baseline for Future Implementations)
+
+- Deployment: Vercel
+- Primary content DB: Vercel Postgres (stores article body/metadata/status)
+- Media assets: Vercel Blob (stores images/videos/etc. binary, returns publicly accessible URLs)
+- Release latency: near‑second visibility, allows caching + revalidation (proactively `revalidateTag/revalidatePath` after publishing)
+- Permission requirement: **Publishing / uploading / editing is only allowed for yourself** (must enforce authentication; a “hidden entry” is not a security mechanism)
+- Drafts: needed (`draft/published`), with support for **private draft preview / continued editing**
+- Authentication: password session (server validates password → issues **signed HttpOnly Cookie**)
+
+## 0.5 Current Implementation (Implemented)
+
+### Key Files
+- Database schema: `db/schema.sql`
+- DB connection: `src/lib/db.js`
+- Write authentication: `src/lib/writeAuth.js`, `src/lib/writeGuard.js`
+- Health check: `app/api/health/route.js`
+- Write APIs:
+ - `app/api/write/session/route.js` (login / logout / session)
+ - `app/api/write/posts/route.js` (draft upsert + draft list)
+ - `app/api/write/posts/publish/route.js` (publish upsert)
+ - `app/api/write/posts/[slug]/route.js` (read draft/published for editor loading)
+ - `app/api/write/assets/route.js` (upload to Blob and backfill URL)
+- Public read path: `src/lib/posts.js` (DB only: `published`)
+- Editor: `src/components/WritePage.jsx` (login / draft / publish / upload / export)
+
+### Initialization Steps (What you need to do manually the first time)
+1) **Create tables**: Vercel Dashboard → Storage → Postgres → Query, execute `db/schema.sql`
+2) **Configure environment variables (Vercel + local)**
+ - Postgres: `DATABASE_URL`
+ - Blob: `BLOB_READ_WRITE_TOKEN`
+ - Write authentication: `ERII_WRITE_PASSWORD`, `ERII_WRITE_SESSION_SECRET`
+3) **Validate connectivity**: after starting locally, access `GET /api/health` (`db.ok=true` and `blob.ok=true`)
+4) **Login and publish**: open `/write` → enter password in settings panel → draft / submit / upload works
+
+## 1. Current State and Constraints
+
+### Current State (Code Level)
+- Article source: **DB (`published`) single source**
+- Reading logic: `src/lib/posts.js` (DB only + `unstable_cache`, refreshed after publishing via `revalidateTag/revalidatePath`)
+- Display page: `app/blog/[slug]/page.jsx` (server reads `getPostData(slug)`)
+- Editor: `src/components/WritePage.jsx` (password‑session login; draft/publish writes to DB; image upload to Blob; still supports download/copy MDX)
+
+### Key Constraints (Vercel)
+- The file system in Vercel/Serverless runtime **cannot be written persistently**.
+- Therefore: production cannot rely on “writing local files” as a publishing/storage solution.
+
+**Conclusion**: To achieve “online publishing and display”, a **persistent publishing channel** must be chosen:
+1) Write to a Git repo (visible after triggering a redeploy)
+2) Write to external persistent storage (visible without redeploy)
+
+## 2. Decision Questions (Answer First, Then Choose)
+
+**Confirmed**
+- **Latency**: near‑second visibility, allows caching / revalidation
+- **Content form**: DB/object storage (Postgres + Blob)
+- **Drafts**: `draft/published` + private draft preview / continued editing
+- **Authentication**: password session (HttpOnly Cookie)
+- **Security model**: only self can write (publish / upload / draft read requires auth)
+
+**To be confirmed (will affect implementation scope)**
+- Need draft list/search/delete/recycle bin (cross‑device retrieval & management)?
+- Need version history/rollback/scheduled publishing (higher complexity)?
+
+## 3. Option A (Recommended Starting Point): Publish = Submit to GitHub `content/`, Vercel Auto‑Redeploys
+
+### Suitable Scenarios
+- You already drive the whole site with `content/*.mdx`;
+- You accept waiting for a build/deploy after publishing;
+- You want “minimal changes” while preserving Git history.
+
+### Core Flow
+1) `/write` frontend sends `fullMdx` to `POST /api/publish`
+2) Server route handler calls the GitHub Contents API:
+ - create/update `content/.mdx`
+ - commit to the specified branch (usually `main`)
+3) Vercel watches repo changes → auto build/deploy
+4) After deployment, the new article automatically appears at `/blog/` and in the homepage list
+
+### What Needs to Be Done (Implementation Highlights)
+- Add route handler: `app/api/publish/route.js`
+- Server‑side env vars (visible only to server):
+ - `GITHUB_TOKEN` (PAT or GitHub App token)
+ - `GITHUB_OWNER` / `GITHUB_REPO` / `GITHUB_BRANCH`
+ - `ERII_WRITE_PASSWORD` (or separate `WRITE_TOKEN` to protect the publish endpoint)
+- Slug strategy (suggest predictable & stable):
+ - generated from title/date (return final slug on publish)
+ - handle duplicate slugs (suffix `-2`, etc.)
+- Security:
+ - Token never sent to client
+ - API must authenticate + rate‑limit (at least limit origin/password/simple rate limit)
+ - Validate `slug` to prevent path traversal (allow only `[a-z0-9-]`, etc.)
+- UX:
+ - After publish show “submitted, waiting for Vercel deployment…”
+ - Optional: return commit SHA or expected article URL
+
+### Pros & Cons
+**Pros**
+- Minimal changes: hardly any modifications to `src/lib/posts.js` or existing pages
+- Content naturally versionable, auditable, PR‑able
+
+**Cons**
+- Not second‑level: publishing ≈ triggering a deploy
+- Requires GitHub Token and endpoint authentication (security mandatory)
+
+## 4. Option B (Recommended for the Long Term): Publish = Write to Persistent Storage, Pages Read from Storage (Achievable Second‑Level Visibility)
+
+### Suitable Scenarios
+- You want “publish and immediate visibility”
+- You don’t mind moving the content source from `content/` to external storage
+
+### Sub‑options
+
+#### B1. Vercel Postgres (Most Structured‑Friendly)
+Recommended as the primary store for **articles and metadata**.
+
+**Suggested schema (minimum viable, favor KISS/YAGNI)**
+- `posts`
+ - `id`: uuid (PK)
+ - `slug`: text (unique, used in URL; suggest `YYYY-MM-DD-` to avoid transliteration complexity)
+ - `title`: text
+ - `description`: text (nullable)
+ - `date`: date (for sorting/display)
+ - `tags`: text[] (or jsonb)
+ - `cover_url`: text (nullable, from Blob)
+ - `content_md`: text (Markdown/MDX source)
+ - `status`: text (`draft` | `published`)
+ - `published_at`: timestamptz (nullable)
+ - `created_at` / `updated_at`: timestamptz
+
+*(Optional) `assets` for media metadata (not required but useful later for an asset library / reuse / deletion)*
+- `assets`
+ - `id`: uuid (PK)
+ - `blob_url`: text
+ - `pathname`: text (e.g. `images/.png`)
+ - `content_type`: text
+ - `bytes`: int
+ - `created_at`: timestamptz
+
+**Caching & revalidation** (to meet “second‑level visibility + allow caching”)
+- Read: use `unstable_cache` (or Next.js tag‑based data cache) and tag queries:
+ - List: `posts`
+ - Single: `post:${slug}`
+- Write/Publish: after success, call `revalidateTag("posts")`, `revalidateTag(\`post:${slug}\`)` (or `revalidatePath`)
+
+#### B2. Vercel Blob + DB/KV (Better for Large Content)
+Recommended as the primary store for **media assets** (images/videos/attachments).
+
+**Editor UX improvements** (solving the pain of manually placing files in `public/`)
+- Editor provides “upload image” button → `POST /api/assets` uploads to Blob
+- API returns `url` → automatically insert into content: `` or set as `cover_url`
+
+*Note*: **Do not** store image binaries in Postgres (possible but hurts backup/migration/performance/CDN).
+
+#### B3. Vercel KV (Redis)
+- Can store small content or draft cache
+- Large content (long articles) may not be suitable as a long‑term primary store (depends on strategy & cost)
+
+### B's API Draft (MVP Implemented, Current Path)
+
+**Auth (self only)**
+- `POST /api/write/session`: submit `ERII_WRITE_PASSWORD` (`{ password }`), on success issue **signed HttpOnly Cookie** (session)
+- `DELETE /api/write/session`: clear cookie
+- `GET /api/write/session`: query session status
+
+**Media upload (Blob)**
+- `POST /api/write/assets`: upload file → write to Blob → return `{ url, blob }`
+ - Constraints: MIME whitelist (`image/*`, optional `video/*`), size limit, auth required
+
+**Article write (Postgres)**
+- `GET /api/write/posts?status=draft|all`: draft list (session only)
+- `POST /api/write/posts`: draft upsert (returns `slug` for continued editing)
+- `POST /api/write/posts/publish`: publish upsert (returns `slug`; triggers `revalidateTag/revalidatePath`)
+- `GET /api/write/posts/:slug`: read draft/published (session only, for continued editing)
+- `POST /api/posts/:id/publish`: set to `published` + `published_at` + `revalidateTag`
+
+**Draft workflow (MVP suggestion)**
+1) Enter `/write`: automatically create a draft or open “last draft”
+2) While editing: debounce `PATCH` save every N seconds or after idle
+3) Management: settings drawer offers “draft list / open / delete”
+4) Publish: on success redirect to `/blog/` and trigger `revalidateTag("posts")` + `revalidateTag(\`post:${slug}\`)`
+
+### B's Page Modifications (Migrate from File Source to DB)
+- `src/lib/posts.js`
+ - `getSortedPostsData()`: change to query Postgres (only `published`)
+ - `getPostData(slug)`: change to query Postgres by `slug` (only `published` or optionally support draft preview)
+- `app/blog/[slug]/page.jsx`: keep rendering structure, swap data source to DB
+- `app/page.jsx`, `app/blog/page.jsx`: lists also use DB
+- `/write` editor:
+ - Upload button calls `POST /api/assets`, backfills URL into body/cover field
+ - Publish button calls `POST /api/posts/:id/publish` (second‑level visibility)
+
+### Pros & Cons
+**Pros**
+- Can achieve “second‑level publish & display”
+- No redeploy dependency
+
+**Cons**
+- Larger refactor: need to rewrite content reading chain and migrate existing `content/*.mdx`
+- Must handle backup, migration, cost, and permission management
+
+## 5. Option C (Optional): Integrate a Headless CMS (Sanity / Contentful etc.)
+
+**Suitable scenarios**
+- You prefer the CMS to handle the “content system” (editing, drafts, review, asset management, versioning, etc.)
+- The site side only handles rendering & display
+
+**Cons**
+- Introduces an external system and learning curve; differs significantly from the current “file‑as‑content” approach
+
+## 6. Security & Risk Checklist (Consider Regardless of Choice)
+
+1) **Write capability auth (self only)**: protect `/write` and all write endpoints (publish / save draft / upload Blob).
+ - Lightest recommendation: `ERII_WRITE_PASSWORD` (env var) + login returns HttpOnly Cookie (`SameSite=Strict`, `Secure`)
+ - Cookie suggestions:
+ - Name with `__Host-` prefix (e.g. `__Host-erii_session`), fixed `Path=/`
+ - Content is a **signed, verifiable** session (includes expiration), signed with `ERII_WRITE_SESSION_SECRET`
+ - Use constant‑time password comparison to avoid side‑channel leaks; add rate limiting / delay to login endpoint
+ - Middleware at Edge intercepts `/write` and `/api/*` (write) routes: unauthenticated requests get 401 or redirect
+ - API also validates `Origin` (prevent CSRF) and applies basic rate limiting (prevent brute‑force)
+2) **Input validation**
+ - Restrict `slug` charset, forbid `../`
+ - Limit body size to avoid abuse
+3) **MDX risk**
+ - Current display pages render MDX with `next-mdx-remote/rsc`; if you ever allow non‑self publishing, decide whether JSX/component capability is permitted
+4) **Rate limiting**
+ - Prevent brute‑force password attempts and API abuse
+5) **Cost risk**
+ - Blob uploads can be abused (must auth)
+ - Postgres and Blob have quotas and billing; recommend simple file‑size limits and MIME whitelists
+
+## 7. Recommended Path (Pragmatic)
+
+You’ve indicated a preference for a “smooth writing experience + integrated media upload”, so the recommendation is to go straight with:
+
+1) **Option B (Vercel Postgres + Vercel Blob)**: near‑second visibility, editor supports one‑click upload and link insertion;
+2) **Option A (GitHub commit)** as a fallback: enable when you need content stored as files with Git history.
+
+## 8. (Todo) Implementation Checklist for the Next Real Build
+
+- [ ] Draft management scope: list / open / delete / recycle bin needed?
+- [ ] Session strategy: cookie shape, signing method, expiration, `AUTH_SECRET` rotation policy
+- [ ] Choose: B (Postgres + Blob) as primary, A as fallback
+- [ ] Define: slug rules + conflict strategy
+- [ ] Define: authentication method (password session vs OAuth)
+- [ ] UI: publish button states, error messages, completion feedback
+- [ ] Cache: tag‑based cache + `revalidateTag/revalidatePath`
+
+### MVP Implementation Order (Suggested)
+
+1) **Base resources**
+ - Provision Postgres + Blob in the Vercel console, set environment variables for local/preview/production
+
+2) **Auth closure** (lock down write ability first)
+ - Implement `POST /api/auth/login`, `POST /api/auth/logout`
+ - Middleware protects `/write` and write APIs (publish / save / upload)
+
+3) **Media upload** (key UX)
+ - Implement `POST /api/assets`: upload → Blob → return URL (auto‑insert in editor)
+
+4) **Draft CRUD**
+ - Implement `POST /api/posts`, `GET/PATCH/DELETE /api/posts/:id`, `GET /api/posts?status=draft`
+ - Add “auto‑save (debounce) + draft list open” in editor
+
+5) **Publish & display**
+ - Implement `POST /api/posts/:id/publish` + `revalidateTag`
+ - Migrate `src/lib/posts.js` from `fs` to Postgres (initially read only `published`)
+
+6) **Migrate historic articles** (optional)
+ - Write a one‑off script to import existing `content/*.mdx` into Postgres, or use a short‑term dual‑source read (not recommended long‑term)
\ No newline at end of file
diff --git a/translations/en/findings.md b/translations/en/findings.md
new file mode 100644
index 0000000..77866da
--- /dev/null
+++ b/translations/en/findings.md
@@ -0,0 +1,40 @@
+# Findings
+
+## 2026-03-15
+
+- The `posts` table currently only holds a single status per article, with `published` and `draft` being mutually exclusive; the public site only reads `posts(status='published')`.
+- `WritePageV2` currently auto‑saves drafts by calling `/api/write/posts`, which writes the target article directly as a `draft`.
+- A fix has been applied for published articles: auto‑save is prevented from silently demoting `published` to `draft`.
+- The new behavior users want is not “restoring the old logic,” but adding the ability to have an “editing draft for a published article.”
+- Although `post_revisions` can store historical content, it lacks fields such as `slug` and `date`, making it unsuitable as the primary storage for recoverable editing drafts.
+- A more reasonable approach is to add a separate editing‑draft storage and have `/write?slug=` load that draft preferentially.
+- `pnpm build` has reached the page pre‑rendering stage, but fails because the existing `/admin/comments` in the repository lacks a `Suspense` wrapper around `useSearchParams()`. This is unrelated to the current changes.
+- Neon MCP is currently unavailable; the error is OAuth `invalid_grant: Invalid refresh token`. Additionally, `codex mcp login neon` returns `No authorization support detected`.
+- Using the Neon connection string in the project, we directly executed `db/migrations/20260315_post_working_drafts.sql` against the database and verified that `to_regclass('public.post_working_drafts') = post_working_drafts`.
+
+## 2026-03-16
+
+- The current repository’s content model already includes external source fields: `editor_source`, `source_ref`, `source_updated_at`.
+- The constraint on `posts.editor_source` already includes `import`, so the Juejin import MVP does not need to add a new source enum first.
+- `content_sync_jobs.provider` currently only allows `notion`. Reusing the sync‑job table directly would require an extra migration; the MVP is better served by a manual import API.
+- By requesting a public Juejin article page from the server, we confirmed that the SSR HTML can be fetched directly, and the page contains `window.__NUXT__`, `article_info`, and `web_html_content`.
+- This means that Juejin import can prioritize parsing the public page, without relying on logged‑in state, cookies, or bidirectional sync in the MVP.
+- The imported body still needs to be converted from HTML to Markdown, as the current writing entry point is the ByteMD editor.
+- If duplicate imports directly overwrite existing articles, published content may be unintentionally affected; the MVP should first deduplicate by `source_ref` and return the existing draft.
+- Public Juejin author pages also return SSR HTML, showing the author’s article list, `article_id`, `post_article_count`, and public pagination links in the form `?cursor=`.
+- This makes “scanning all public articles from an author’s home page in bulk, then importing them one by one” a feasible main flow for the MVP.
+- For the scenario “importing one’s own articles,” the public author page is sufficient; there is no need to integrate Juejin’s logged‑in state directly into the blog backend.
+- In a Next/Turbopack environment, `turndown-plugin-gfm` needs to be imported as a namespace (`import * as …`); default import causes a build error.
+- Bulk scanning validation passed: example author `312692511089736` successfully scanned 15 articles with `maxPages=2, maxArticles=15`.
+- Single‑article extraction validation passed: example article `7250317954993897528` correctly extracts title, description, date, cover, and Markdown content snippet.
+- The build has confirmed that the new code can pass the compilation stage; `pnpm build` still stops at the existing `/admin/comments` issue where `useSearchParams()` lacks a `Suspense` wrapper.
+- To avoid writing real content before verification, no real import into production/development databases was performed this time.
+- Real single‑article import test succeeded: article `7548595210558767138` was imported as a draft and successfully published.
+- Real small‑batch test succeeded: author `2936108653217451` completed bulk scanning and import with `maxPages=1, maxArticles=3`.
+- A critical issue was discovered during the process: when an imported article is published in the editor, `editorSource/sourceRef/sourceUpdatedAt` are lost, causing subsequent batch imports to fail deduplication.
+- This was fixed by persisting source metadata in `WritePageV2`, and verification shows that re‑importing correctly results in `skipped`.
+- To avoid retaining test junk data, the mistakenly created duplicate draft `打造属于你的前端沙盒-2` has been deleted.
+- Full public article import verification completed: author `2936108653217451` scanned 96 public articles, all of which are now importable with no failures.
+- The remaining failures in the first full import were not due to content conversion but because some articles returned ByteDance’s WAF `Please wait...` JavaScript challenge page.
+- This challenge page contains `wci/cs` parameters; by solving the SHA‑256 nonce locally to generate a `_wafchallengeid` cookie, the content page can be fetched immediately afterward.
+- After adding this handling in `fetchJuejinHtml`, the last originally‑failing article `7536319934545412139` was also successfully imported
\ No newline at end of file
diff --git a/translations/en/progress.md b/translations/en/progress.md
new file mode 100644
index 0000000..5744bfb
--- /dev/null
+++ b/translations/en/progress.md
@@ -0,0 +1,46 @@
+# Progress
+
+## 2026-03-15
+
+- Read `openspec/AGENTS.md`, `openspec/project.md`
+- Checked active changes: `add-admin-dashboard`, `edit-published-posts`
+- Reviewed the current writing page, publishing API, and content service
+- Confirmed the current state: published articles are not automatically taken offline, but unpublished edits do not go into the draft box
+- Preparing a new OpenSpec proposal defining the behavior of “independent editing drafts for published articles”
+- Created change: `openspec/changes/add-published-post-working-drafts/`
+- Completed `openspec validate add-published-post-working-drafts --strict`
+- Added `post_working_drafts` schema, migration file, working‑draft service layer, and save API
+- Integrated auto‑save on the edit page, flush before closing, and cleanup of working drafts after publishing
+- Updated the backend draft list to support displaying “published article revisions”
+- Completed ESLint checks for related files
+- Tried `pnpm build`; the build failed at the existing `/admin/comments` prerender issue, which was not introduced by this change
+- Successfully executed `20260315_post_working_drafts.sql` via direct Neon connection; database table created
+- Confirmed Neon MCP still has local OAuth credential issues, not fixed in this task
+
+## 2026-03-16
+
+- Read the `planning-with-files` documentation and added planning records for this proposal
+- Ran `openspec list` / `openspec list --specs`, confirming there are unarchived changes in the repository but no formal specs yet
+- Re‑examined `db/schema.sql`, `contentService.js`, and the Notion adapter / sync flow, confirming the Juejin import can reuse the existing content platform structure
+- Verified on the server that public Juejin article pages return SSR HTML containing parsable `article_info` / `web_html_content`
+- Created change directory: `openspec/changes/add-juejin-import/`
+- Added `proposal.md`, `design.md`, `tasks.md`, and `specs/juejin-import/spec.md`
+- Added verification of the public author page structure, confirming the presence of an article list and `cursor` pagination clues for batch import
+- Shifting the proposal scope from “single‑article import” to “author‑homepage batch import as primary, single‑article import as secondary”
+- Added dependencies: `cheerio`, `turndown`, `turndown-plugin-gfm`
+- Implemented `src/lib/content/htmlToMarkdown.js`, `src/lib/content/adapters/juejinAdapter.js`, `src/lib/content/juejinImport.js`
+- Added API: `app/api/write/import/juejin/route.js`
+- Integrated a “Import Juejin” entry, batch/single mode toggle, and import result panel into `WritePageV2`
+- Passed `pnpm lint` (retaining the repository’s existing three `` warnings)
+- Verified that the author‑page scanning and single‑article content extraction scripts run correctly
+- Passed the compilation stage of `pnpm build`; the final build still fails on the repository’s existing `/admin/comments` Suspense issue
+- Completed a real single‑article import test: `7548595210558767138` imported, published, and displayed successfully
+- Completed a real small‑batch test: author `2936108653217451` scanned 3 articles, first‑time import succeeded
+- Identified and fixed the “missing Juejin source metadata after publishing” issue
+- Cleaned up duplicate test draft `打造属于你的前端沙盒-2`
+- Re‑ran the same batch test, result: `scanned=3, imported=0, skipped=3, failed=0`
+- Ran a full public‑article import for author `2936108653217451`; first full run result: `scanned=96, imported=85, skipped=3, failed=8`
+- Re‑ran imports for the remaining failed articles, pinpointing the last failure as Juejin returning a JS challenge/waf page instead of article content
+- Added automatic handling of Juejin `_wafchallengeid` challenges in `juejinAdapter`
+- Successfully re‑imported the final failed article `7536319934545412139`
+- Completed a full reconciliation: author `2936108653217451` now shows `scanned=96, imported=0, skipped=96, failed=0`
\ No newline at end of file
diff --git a/translations/en/task_plan.md b/translations/en/task_plan.md
new file mode 100644
index 0000000..df5c1f0
--- /dev/null
+++ b/translations/en/task_plan.md
@@ -0,0 +1,57 @@
+# Task Plan
+
+## Goal
+
+Implement that unpublished edits to a published article go into the drafts folder, without affecting the live published content; only after manual publishing is the blog content updated.
+
+## Phases
+
+- [complete] 1. Review the existing writing/publishing/draft data flow and constraints
+- [complete] 2. Design an independent edit‑draft model and API for published articles
+- [complete] 3. Write an OpenSpec change proposal and task list
+- [complete] 4. Implement backend and frontend changes after proposal approval
+- [in_progress] 5. Verify the full workflow of editing, closing, drafting, and republishing
+
+## Errors Encountered
+
+| Error | Attempt | Resolution |
+|-------|---------|------------|
+| `openspec/specs/write-editor/spec.md` does not exist | 1 | Switch to reading the existing change description under `openspec/changes/`; the project currently has no archived specs |
+
+## Notes
+
+- The current repository has unarchived changes: `add-admin-dashboard`, `edit-published-posts`
+- The current workspace contains user uncommitted changes; avoid touching unrelated files
+
+## 2026-03-16 Current Task
+
+### Goal
+
+Create an OpenSpec proposal for “Importing Juejin articles into the blog”, with scope narrowed to the MVP of “Manually importing public articles as drafts”.
+
+### Phases
+
+- [complete] 1. Review the existing content platform, Notion sync pipeline, and OpenSpec constraints
+- [complete] 2. Validate whether Juejin public article pages can be extracted for structured content on the server side
+- [complete] 3. Write the `add-juejin-import` proposal, design, and task list
+- [complete] 4. Run strict OpenSpec validation and compile conclusions
+- [complete] 5. Implement Juejin adapter, import service, writing API, and `/write` entry point
+- [complete] 6. Perform no‑side‑effect verification and organize remaining real import validation items
+- [complete] 7. Complete a real single‑article import and small‑batch deduplication verification
+- [complete] 8. Complete full import of public articles, handling Juejin risk‑control challenges and compatibility
+
+### Errors Encountered
+
+| Error | Attempt | Resolution |
+|-------|---------|------------|
+| `openspec list --specs` returned `No specs found.` | 1 | Indicates that the current repository has no archived formal specs yet; this time directly add a capability delta under the changes directory. |
+
+### Notes
+
+- Juejin public article pages currently return SSR HTML directly, containing `window.__NUXT__`, `article_info`, and `web_html_content`.
+- The `posts` table already supports `editor_source='import'`, `source_ref`, `source_updated_at`, sufficient for MVP source tagging.
+- `content_sync_jobs.provider` currently only allows `notion`, so the MVP does not introduce a sync job table and uses the manual import API first.
+- Juejin public author pages show article lists with `?cursor=` pagination links, so the proposal should be upgraded to a “batch import public articles” primary workflow.
+- The core code is implemented, but import writes to the real database have not been executed yet, to avoid contaminating existing article data before confirmation.
+- Real database verification has been completed, confirming the deduplication pipeline works.
+- Completed full import of 96 public articles for author `2936108653217451`; final review result: `skipped=96, failed=0`.
\ No newline at end of file
diff --git a/translations/en/teamspeak-optimized.md b/translations/en/teamspeak-optimized.md
new file mode 100644
index 0000000..0555856
--- /dev/null
+++ b/translations/en/teamspeak-optimized.md
@@ -0,0 +1,117 @@
+# TeamSpeak Installation Guide
+
+## Before Starting
+
+Our TS server: **ts.ricardoiyu.top** (just copy‑paste to connect)
+
+If you already have TeamSpeak installed, you can jump straight to the server recommendation page.
+
+---
+
+## TeamSpeak 3
+
+### Download
+
+Go to the official website [https://teamspeak.com/zh-CN/](https://teamspeak.com/zh-CN/). Make sure you download from this official domain—don’t trust any other “Chinese site”.
+
+**Why?** Pirated or tampered clients will be blacklisted by the official servers, and you won’t be able to connect at all.
+
+On the website, click **Download**, choose **TS3 Client**, and click the download button.
+
+### Installation
+
+Double‑click the installer and click **Next** all the way.
+
+When you reach the license terms, you must scroll to the very bottom to check “I agree”. There’s no way around it.
+
+Continue with **Next**, select **Install just for me**.
+
+Choose the installation location (the default is fine).
+
+**Important:** Uncheck **overwolf**, then click **Install**. Overwolf does nothing useful here, so don’t install it.
+
+Click **Finish**; TS3 will launch automatically.
+
+### First Launch
+
+A license agreement pops up; scroll to the bottom and click **I accept**.
+
+A TS6 recommendation window may appear—just close it; it only shows up once.
+
+Next comes the myTeamSpeak registration/login window. If you only use TS3, close it. If you want to register, this account can store your server bookmarks and keys, so you won’t lose them when you switch computers.
+
+After closing, a nickname prompt might appear. You can enter any name you like or simply skip it; it won’t affect usage.
+
+### Localization
+
+Close TS3, then open the Chinese localization pack.
+
+Click **install**, then click **yes**—that’s it.
+
+Reopen TS3 and the Chinese interface will appear.
+
+### Advanced Settings
+
+**Audio Output**
+Open TS3, go to **Tools - Settings - Audio Output**.
+In the Output Device list, select your headphones or speakers, then click **apply** to save.
+If you have multiple devices, you can add separate configuration profiles on the left side and set each one individually.
+
+**Audio Input**
+Do the same for your microphone—select it and configure as needed.
+
+After you’ve configured everything, you can connect to the server and start chatting.
+
+Our server **ts.ricardoiyu.top** already has a music bot that streams random NetEase Cloud Music tracks 24 / 7.
+
+---
+
+## TeamSpeak 6
+
+### Why Use TS6?
+
+Compared with TS3, TS6 adds the following:
+- A more modern UI
+- Private messaging
+- Image sharing
+- Free P2P screen sharing (no quality limits)
+
+### Download
+
+The download process is the same as for TS3, or you can simply search **Teamspeak** in the Microsoft Store and install it from there.
+
+### Installation
+
+Double‑click the installer; the steps are similar to TS3, but there’s no overwolf bundle, so you can safely proceed to the next step.
+
+Choose the second option, pick the installation location, and click **Next** to install.
+
+Click **Finish**; the software will start automatically.
+
+### First Launch
+
+TS6 has one drawback: you must register a myTeamSpeak account to use it; it isn’t as flexible as TS3.
+
+Click **Get started**, scroll to the bottom, and accept the terms.
+
+Registration is simple—just an email address, no real‑name verification. After filling out the form, go to your email and click the verification link.
+
+After verification, click **Continue**, accept the terms, pick a theme, and click **Finish** to log in automatically.
+
+### Localization
+
+Click the gear icon to open Settings, then go to **Appearance**.
+
+Find **language** and select **Chinese**.
+
+Click **confirm**; after a restart, the interface will be in Chinese.
+
+Once localized, you can explore TS6 on your own.
+
+---
+
+## Conclusion
+
+TS3 is lightweight and flexible, while TS6 offers more features and a modern UI. Choose the one that fits your needs.
+
+Feel free to join our server **ts.ricardoiyu.top** and have fun
\ No newline at end of file