Skip to content

release: dev → main (telegram prefix + tryon ux + content-studio)#537

Merged
cocoyoon merged 43 commits into
mainfrom
dev
May 16, 2026
Merged

release: dev → main (telegram prefix + tryon ux + content-studio)#537
cocoyoon merged 43 commits into
mainfrom
dev

Conversation

@cocoyoon
Copy link
Copy Markdown
Member

Summary

dev에 쌓인 변경분을 main으로 릴리즈.

포함된 PR

Test plan

  • Production deploy 후 [decoded-monorepo] 접두사로 텔레그램 알림 수신 확인
  • VTON UX QA — try-on flow + 모달 z-index
  • Content studio DB persistence 검증

cocoyoon and others added 30 commits May 7, 2026 17:27
* feat(raw_posts): StarStyle.com adapter (#466)

WordPress SSR fashion 사이트(starstyle.com) 어댑터 추가. wots(#465) 인프라
재사용 — discovery_target='raw_posts' 글로벌 피드 모델, PrelabeledData 로
vision 단계 우회.

핵심 차이: per-item 사진 URL 이 없어 thumbnail_url=None → items_thumbnail
processor 가 spots bbox 로 hero crop fallback path 사용. brand/title 분리는
보수적으로 product 통째로 (verify 단계 admin 처리).

- adapters/_starstyle_html.py: parse_post / parse_sitemap /
  decode_skimresources (skim affiliate 디코드)
- adapters/starstyle.py: thin httpx wrapper, fetch + sitemap discover
- scripts/backfill_starstyle_posts.py: sitemap → JSONL streaming → PostgREST
  bulk INSERT (wots 미러)
- service.rs: parse_starstyle_source — slug-sp{ID} 형식 검증
- admin UI: PLATFORM_FILTERS / PLATFORM_TABS / DiscoveryPipelineCard / URL builder
- migration: pipeline_settings starstyle row (모든 cycle OFF)
- 24 단위 테스트 추가 (parser + adapter + prelabeled round-trip)

Closes #466.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(starstyle): force HTTPS on og:image URL (#466)

starstyle 의 og:image 가 http:// 로 노출되는데 admin 은 HTTPS 라
mixed-content 로 이미지가 차단되던 문제. CDN 자체는 HTTPS 정상이라
파서 단계에서 https 강제 변환.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#484)

starstyle 의 og:image 는 admin (HTTPS) 에서 직접 hotlink 가 막힌다:
  - HTTPS 요청 → 301 redirect to HTTP → mixed-content 차단
  - HTTP 요청 → User-Agent / Referer 가 비어 있으면 403

백필 시점에 boto3 로 upstream 이미지 다운로드 → R2 (RAW_POSTS_R2_BUCKET) 에
``starstyle/{shard}/{external_id}.jpg`` 키로 업로드하고, raw_posts.image_url
을 R2 public URL 로 저장한다. admin 은 R2 직접 fetch (HTTPS clean, hotlink
없음) → Vercel image-proxy 우회.

- HEAD 로 R2 객체 존재 확인 → 재실행 시 중복 업로드 skip (idempotent)
- 다운로드/업로드 실패 시 upstream URL 로 fallback (image_url 그대로)
- RAW_POSTS_R2_* env 미설정 시 mirror skip + warning
- PostData @DataClass(frozen=True) → frozen 제거 (image_url 교체 위해)

검증: 500 row 재백필, 479/500 R2 mirrored. 샘플 R2 URL HTTPS 200 확인.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts:
#	packages/ai-server/scripts/backfill_starstyle_posts.py
* release: starstyle R2 mirror fix (#466) (#485)

* feat(raw_posts): StarStyle.com adapter (#466) (#481)

* feat(raw_posts): StarStyle.com adapter (#466)

WordPress SSR fashion 사이트(starstyle.com) 어댑터 추가. wots(#465) 인프라
재사용 — discovery_target='raw_posts' 글로벌 피드 모델, PrelabeledData 로
vision 단계 우회.

핵심 차이: per-item 사진 URL 이 없어 thumbnail_url=None → items_thumbnail
processor 가 spots bbox 로 hero crop fallback path 사용. brand/title 분리는
보수적으로 product 통째로 (verify 단계 admin 처리).

- adapters/_starstyle_html.py: parse_post / parse_sitemap /
  decode_skimresources (skim affiliate 디코드)
- adapters/starstyle.py: thin httpx wrapper, fetch + sitemap discover
- scripts/backfill_starstyle_posts.py: sitemap → JSONL streaming → PostgREST
  bulk INSERT (wots 미러)
- service.rs: parse_starstyle_source — slug-sp{ID} 형식 검증
- admin UI: PLATFORM_FILTERS / PLATFORM_TABS / DiscoveryPipelineCard / URL builder
- migration: pipeline_settings starstyle row (모든 cycle OFF)
- 24 단위 테스트 추가 (parser + adapter + prelabeled round-trip)

Closes #466.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(starstyle): force HTTPS on og:image URL (#466)

starstyle 의 og:image 가 http:// 로 노출되는데 admin 은 HTTPS 라
mixed-content 로 이미지가 차단되던 문제. CDN 자체는 HTTPS 정상이라
파서 단계에서 https 강제 변환.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: retrigger CI with bump label

* fix(starstyle-backfill): mirror hero images to R2 before INSERT (#466) (#484)

starstyle 의 og:image 는 admin (HTTPS) 에서 직접 hotlink 가 막힌다:
  - HTTPS 요청 → 301 redirect to HTTP → mixed-content 차단
  - HTTP 요청 → User-Agent / Referer 가 비어 있으면 403

백필 시점에 boto3 로 upstream 이미지 다운로드 → R2 (RAW_POSTS_R2_BUCKET) 에
``starstyle/{shard}/{external_id}.jpg`` 키로 업로드하고, raw_posts.image_url
을 R2 public URL 로 저장한다. admin 은 R2 직접 fetch (HTTPS clean, hotlink
없음) → Vercel image-proxy 우회.

- HEAD 로 R2 객체 존재 확인 → 재실행 시 중복 업로드 skip (idempotent)
- 다운로드/업로드 실패 시 upstream URL 로 fallback (image_url 그대로)
- RAW_POSTS_R2_* env 미설정 시 mirror skip + warning
- PostData @DataClass(frozen=True) → frozen 제거 (image_url 교체 위해)

검증: 500 row 재백필, 479/500 R2 mirrored. 샘플 R2 URL HTTPS 200 확인.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(release): backend bump ai=1.5.1 api=0.10.1 [skip ci]

* feat(raw_posts): R2 cleanup on raw_post delete (#466)

raw_post 삭제 시 image_url 이 raw_posts R2 객체이고 verified posts 가
같은 URL 을 참조하지 않으면 R2 객체도 같이 삭제. verify 후에는 posts.
image_url 가 raw_post.image_url 그대로 복사되므로 (#333) — verified post
가 참조 중이면 보존해야 깨지지 않는다.

또한 storage 명명을 db 패턴(db / assets_db) 에 맞춤:
  - storage_client → operation_storage
  - 신규 RAW_POSTS_R2_* → assets_storage
  - AppConfig.storage / assets_storage, AppState.operation_storage /
    assets_storage 일관 매핑.

config:
  - StorageConfig 두 개 (storage / assets_storage), 별도 R2 버킷 wiring
  - RAW_POSTS_R2_{ACCOUNT_ID, ACCESS_KEY_ID, SECRET_ACCESS_KEY, BUCKET, PUBLIC_URL}

service::delete_item:
  - 시그니처 (assets_db, prod_db, storage, public_url, id) — 4개의 DI
  - raw_post.image_url 추출 → DELETE 진행
  - extract_assets_r2_key 로 R2 key 추출 (public_url prefix strip)
  - posts.image_url COUNT — 0 이면 storage.delete(key) 호출
  - 실패는 best-effort warn (이미 raw_post 삭제는 성공)

테스트: 4개 단위 (key 추출 - prefix/trailing slash/other domain/empty url).

solutions/tests.rs 의 AdminSolutionListQuery 빌드에 has_url=None 추가
(unrelated 사전 컴파일 에러 — 본 PR 의 cargo test --lib 통과 위해 보강).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: decoded-ci <ci@decodedcorp.com>
Store the judged image URL with vision filter audit rows so Instagram skip decisions can still render admin thumbnails after raw_posts cleanup.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
작업 1 (마이그레이션, 1080 row) 후속 — 신규 verify 데이터도 동일한 single-source
컨벤션 (r2.decoded.style/posts/{post_id} / r2.decoded.style/items/{solution_id})
유지하도록 verify_raw_post 흐름 수정.

기존: raw_post.image_url (assets R2 dev pub URL) 그대로 posts.image_url 복사
→ 운영 DB 가 dev 도메인 URL 들고 있는 버그.

새 흐름:
  1. prod_txn 진입 전 R2 copy:
     - post_id 사전 생성 → assets_storage.download(raw_key) →
       operation_storage.upload("posts/{post_id}")
     - 각 visible item: solution_id 사전 생성 → items thumbnail 동일 패턴
  2. prod_txn: create_post_from_raw / create_solution_for_verify 에 미리 생성한
     UUID + 새 operation URL 전달 (시그니처에 명시 인자 추가)
  3. assets_txn: raw_post.image_url 도 operation URL 로 갱신 (단일 진실 소스)
  4. 두 트랜잭션 commit 후 raw 객체 best-effort delete (warn 만, orphan 허용)

코드 변경:
  - StorageClient trait 에 `download(key) -> (bytes, Option<content_type>)` 추가
  - CloudflareR2Client GetObject 구현, NotFound 매핑
  - DummyStorageClient stub 구현
  - 신규 `raw_posts/relocate.rs` — relocate_raw_to_operation helper + 4 단위 테스트
  - extract_assets_r2_key → pub(crate) 격상 (relocate 가 재사용)
  - create_post_from_raw 시그니처: post_id, image_url 명시 인자 (내부 Uuid::new_v4 제거)
  - create_solution_for_verify 시그니처: solution_id 명시 인자
  - verify_raw_post: prod_txn 전 R2 copy 단계 + raw cleanup post-commit

실패 처리:
  - copy 도중 실패 → BadRequest/NotFound/ExternalService Err, prod_txn 미시작 (DB 부작용 0)
  - 부분 성공 (hero copy OK / item N copy fail) → op bucket 에 hero orphan
    잔존 (lifecycle rule / 별도 GC out of scope)
  - raw delete 실패 → warn 로깅만, verify 자체는 성공 유지

테스트:
  - 4 단위 (relocate happy/fallback/non-raw-rejection/not-found-propagation)
  - 24 회귀 통과 (parse_source_identifier_*, verify_*)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#494)

Wiki lint 가 docs/wiki/schema/tags.md 에 등록 안 된 태그 거부.
api / ui 만 남겨 dev→main 머지 차단 해제.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts:
#	packages/api-server/src/domains/raw_posts/service.rs
…ard (#496)

* feat(cost-tracking): Gemini API per-call cost tracking + admin dashboard

ai-server 의 Gemini 호출 (raw_post 6 + editorial 7) 모두를 호출 시점에
DB 적립 + 어드민 대시보드에서 일/step/model/pipeline 별 spend 가시화.

핵심 결정:
- 단가는 코드 하드코딩 없음 — `gemini_pricing` DB 테이블 SOT (SCD-2,
  effective_from/to). 어드민 UI 에서 inline upsert.
- per-call `pricing_snapshot` 보존 — 단가 변경 후에도 과거 cost 재현 가능.
- fire-and-forget recorder — 추적이 본 파이프라인 latency/실패에 영향 0.
- 실패도 row 적립 (ok=false, error_class) — 안전·정책 차단, quota,
  parse_error, timeout, no_pricing 등 가시화.
- 5분 in-process pricing cache — 어드민 변경 후 최악 5분 stale 허용.

DB (`supabase-assets/migrations/20260514140000_gemini_cost_tracking.sql`):
- gemini_pricing  (SCD-2, 12 seed row — Pro / Flash / Flash-Lite / Flash-Image / grounding)
- gemini_usage_events (per-call, BRIN + composite index 시간순)
- gemini_spend_daily (view, 대시보드 read-side)

ai-server (`src/services/cost_tracking/`):
- `track_call(step, model, extract, coro)` — coroutine wrapper. usage_metadata
  자동 추출 + estimator (token × DB 단가 × cached 우대) + recorder INSERT.
- raw_post scheduler / post_editorial service 에서 `set_context()` 1회 호출
  → 이후 모든 Gemini 호출이 raw_post_id / post_id / pipeline 자동 적립.
- 13 call site wrap: items/subject/spots/nano_banana(hero+thumbnail)/url_grounded/url_filter
  (raw_post 6), design_spec/image_analysis/item_search/news_research/editorial/review/celeb_search (editorial 7).

api-server (`src/domains/admin/gemini_cost.rs`):
- GET /spend/{daily,by-step,by-model,by-pipeline,today,top-raw-posts}
- GET / POST /pricing  (POST 가 기존 active row close + 새 row insert TX)
- DELETE /pricing/{id} (retire)

web (`/admin/gemini-cost`):
- KPI row (Today / Yesterday / 7d / 30d)
- Daily spend AreaChart (7/14/30d period selector)
- Spend by step / model / pipeline (가로 Bar)
- Top expensive raw_posts (drill-down → /admin/raw-posts/[id])
- Pricing editor (inline upsert + history toggle + retire)
- /admin home 에 `<GeminiCostMini>` 카드 + sidebar 새 "Observability" 그룹

검증:
- migration syntax check (local DB BEGIN; ... ROLLBACK;)
- e2e unit test (scripts/cost_tracking_e2e_test.py) — Pro/Flash/Image/cached/
  failure/no-pricing 6 case 모두 단가 계산 정확 (1000 in × \$1.25/M + 500 out
  × \$10/M = \$0.006250 등 모두 ✓)
- 실측 (scripts/cost_probe.py) — 운영 raw_post 1건 풀 파이프라인:
  fixed \$0.041 + per-item \$0.075 = 3 items \$0.268, 5 items 추정 ~\$0.42
- ai-server import / api-server cargo check / web typecheck + lint 모두 clean

Out of scope (별도 트랙):
- Cloud Billing Catalog API 자동 sync (Vertex 이전 후)
- daily threshold alert (Slack/텔레그램)
- 비용 hot path 최적화 (thumbnail/grounded 감축) — 데이터 적립 후 결정

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(cost-tracking): real-parser wrap verification script

기존 cost_tracking_e2e_test.py 는 `track_call` 을 직접 호출 (mock coroutine).
이 스크립트는 실제 `SubjectParser` 클래스를 import + 호출 → 그 내부에 박힌
wrap 코드 라인이 작동해서 DB row 가 적립되는지 검증한다.

검증 결과 (운영 Pinterest 이미지 1장, ~$0.0005):
  step=subject_parser, model=gemini-2.5-flash, pipeline=raw_post,
  prompt_tokens=813, completion_tokens=98, cost_usd=$0.000489,
  pricing_snapshot={input_token: 0.0000003, output_token: 0.0000025}

→ wrap 코드 경로 + context 자동 전파 + 단가 cache lookup + INSERT 모두
   실제로 작동함을 확인.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(content-studio): add admin content studio

* feat(content-studio): add llm generation and research design

* docs(content-studio): clarify research panel contract

* docs(content-studio): add research panel implementation plan

* Add content studio research workflow

* docs(content-studio): add asset and short-form plan

* feat(content-studio): add asset and short-form schemas

* feat(content-studio): add asset planning helpers

* feat(content-studio): add asset plan api

* feat(content-studio): add asset and short-form panels

* feat(content-studio): add OpenAI image generation with editorial prompts

- Add /api/v1/content/assets/images route (admin-only) that runs OpenAI
  Image API per asset prompt and uploads results to Supabase Storage.
- Switch buildAssetPlan prompts to format-specific editorial / Vogue
  composition guides (4:5 cover, 9:16 spread, 1:1 portrait, 16:9
  thumbnail, 2:3 lookbook). Inject artist + wardrobe references and
  forbid on-image text or logos.
- Add content-studio-assets public storage bucket migration.
- AssetPanel: Generate Images button, image preview rendering,
  Download link per generated asset, failure list surface.

* feat(content-studio): inject research keywords + expand allowed domains

- buildAssetPlan now passes researchRun into image prompts when
  useResearchInCopy is on. Prompts cite trend topics from insights
  and adapt the scene cue based on packet.context (photoshoot, stage,
  runway, street, studio).
- Strengthen FORMAT_COMPOSITION away from solo-portrait bias toward
  full magazine-spread layouts with environment, props, and intentional
  negative space for typography.
- Expand DEFAULT_ALLOWED_DOMAINS so common Firecrawl results
  (instagram, pinterest, medium, dazed, i-d, business-of-fashion, etc.)
  pass the policy filter instead of returning "No allowed research
  sources were found." Configurable via
  CONTENT_STUDIO_RESEARCH_ALLOWED_DOMAINS.

* feat(content-studio): image-to-image edits + clickable insight evidence

Adds OpenAI image-edit endpoint support so generated images preserve
the look of the source post.

- openai-client: new fetchImageBytesFromOpenAIEdit posts multipart to
  /v1/images/edits with up to 4 reference images. generateAssetImage
  now routes to edit endpoint when referenceImageUrls are passed,
  falling back to plain generation on failure.
- /api/v1/content/assets/images route accepts { plan, packet,
  useReferenceImages }. When useReferenceImages is true, packet
  sourceImage and detectedItem thumbnails are forwarded as references.
- AssetPanel: "Use post images (image-to-image)" toggle (default on)
  alongside the Generate Images button.
- ResearchPanel: insight cards now render evidenceRefs as clickable
  domain chips linking to the source URL.

* feat(content-studio): bake headline into generated image (Instagram cover style)

* fix(content-studio): research falls back to top web results if no whitelisted source matches

* fix(content-studio): align asset test assertion with editorial prompt structure

The image prompt now uses packet.hook (concept) instead of packet.title,
matching the editorial magazine prompt format introduced in b55dfb8.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
`users.is_admin=true` 사용자별 raw_post verify 카운트 추적 + 어드민 UI
+ daily-digest 텔레그램 메시지에 통합 (어제 0건 admin 에 부드러운 독촉).

## api-server (`/api/v1/admin/verify-stats`)
- cross-DB join — operation DB users.is_admin + assets DB raw_posts.verified_by/at
- Returns per-admin (today / yesterday / 7d / 30d) + totals + nag_admin_ids
- 어제 0건 → needs_nag=true

## web (`/admin/verify-stats` + home)
- 페이지: KPI cards + per-admin table + nag banner
- /admin home 작은 카드 (오늘 / 어제 / 7d / nag count) — nag 있으면 amber border
- Sidebar Observability 그룹에 항목 추가

## daily-digest (Telegram cron)
- `.github/workflows/daily-digest.yml` — postgresql-client + 2 read-only DSN secret
  (ASSETS_DATABASE_URL_RO, OPERATION_DATABASE_URL_RO; 없으면 silent skip)
- `daily-digest.sh` — psql 2회 (admin list + verified_at GROUP BY)
  → jq zip → DATA_JSON 에 `admin_verify_stats` / `admin_verify_totals`
  → Claude Haiku prompt 에 "needs_nag 인 admin 에 부드러운 독촉 톤" 지시
  → BODY 에 deterministic verify table fallback (LLM 누락 시에도 ⚠️ 표시)

## 검증
- api-server `cargo check` + `cargo fmt --check` ✓
- web `bun run lint` ✓ (verify-stats 신규 파일 clean)
- daily-digest 스크립트 `bash -n` syntax ✓ + jq zip 로직 fixture test ✓

## 필요한 후속 작업
- 두 read-only DSN secret 을 GH repo secrets 에 추가
  (없으면 verify 섹션만 silent skip, 기존 daily-digest 정상 작동)
- 운영팀: read-only DB user 생성 권장 (admin 권한 불필요)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
content-studio (#498) 와 verify-stats (#499) 두 PR 이 같은 위치에 추가:
- admin/handlers.rs: nest 라우터
- /admin/page.tsx: home 카드
- AdminSidebar.tsx: Observability sidebar 항목

둘 다 살리는 방향으로 resolve.
* feat(seed): add editorial/magazine sample data for local dev

post_magazines 2건 + posts 연결로 supabase db reset 시
홈 EditorialMagazine/EditorialCarousel에 샘플 데이터가 표시됨.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(content-studio): add Supabase migration for content_packets/variants tables

Local dev parity — mirrors SeaORM m20260507_000001 so tables exist
without Rust API when SKIP_DB_MIGRATIONS=1. Includes seed data with
one packet and three channel variants (instagram, youtube, x).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore(types): regenerate Supabase types with content_packets/variants

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(content-studio): Supabase direct CRUD for local dev without Rust API

Add db.ts helper (upsert/list/get packets, upsert variants, update
variant status) and wire all content API routes to use Supabase as
fallback when API_BASE_URL is absent or upstream returns 5xx.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(content-studio): lower maxDuration to 60s for Vercel Hobby plan limit

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(types): add content_packets/variants types without full typegen

Manual type addition to avoid overwriting PRD-only tables (item, post,
image) that local dev schema lacks. Replaces reverted full typegen.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(content-studio): destructure auth for proper type narrowing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(content-studio): destructure auth in POST/GET packets for type narrowing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(content-studio): destructure admin in approve/reject/generate-variants

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: cast JSONB fields to Json type for Supabase type compatibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- 메인 스펙: vault 인프라 + Notion 마이그레이션 + GitHub Actions (#512-#515, #518)
- Telegram 봇 스펙: Phase 1 명령형 저장 + Phase 2 그룹 로그 (#516, #517)
- 개인 vault 스펙: claude_volt 생성 + MCP 설정 (#521)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
)

Admin 매거진 draft 페이지에서 썸네일 단독 재생성 트리거 + 파이프라인 에러
가시화. abc34c2f-... 케이스 같이 generate_thumbnail 노드가 실패해도 status=
draft 로 마감되어 admin 이 원인을 못 봤던 결함 해결.

ai-server
- inbound.proto: RegenThumbnail RPC (article_id, hint) 추가, pb2 재생성
- regen_thumbnail_service.py: ARQ task — layout 로드 → generate_thumbnail
  단독 호출 → 결과/에러를 editorial_articles + editorial_article_events 에
  기록 (실패해도 thumbnail_url 보존)
- metadata_servicer: RegenThumbnail handler — 즉시 enqueue 후 응답
- worker: regen_thumbnail_job 등록
- generate_thumbnail._build_prompt: 옵션 hint 파라미터 추가, state 의
  regen_hint 픽업해서 'ADDITIONAL DIRECTION FROM REVIEWER' 블록 주입
- publish_node: graph state 의 error_log 를 editorial_articles.error_log
  jsonb 컬럼에 append (기존 누락 버그 fix — soft-fail 정책은 보존)

api-server
- proto/ai.proto sync + decoded_ai_grpc/client.rs regen_thumbnail wrapper
- POST /api/v1/admin/editorial-articles/{id}/regen-thumbnail 핸들러
  - status guard: draft / failed 만 허용
  - editorial_article_events INSERT (step=regen_thumbnail, note=requested
    + hint snippet) → gRPC enqueue

web
- useRegenThumbnail mutation hook + proxy route
- ArticleErrors 컴포넌트: error_log jsonb 배열을 빨간 배너로 렌더
- drafts/[id]/page.tsx: 썸네일 헤더에 ⟳ 아이콘 버튼 + 클릭 시 인라인
  textarea (선택 hint) + 재생성 트리거. 메인 컬럼 상단에 ArticleErrors 배치

Verification
- ai-server: 실패 경로 E2E 통과 (error_log append + event INSERT + hint
  capture), success 경로 SQL 통과 (OpenAI 빌링 한도로 실 호출 검증은 추후)
- api-server: cargo check + fmt --check 통과
- web: typecheck 신규 에러 0건

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(dx): dev-reset auto-seeds from PRD when PRD_DB_URL available

- seed-from-prod.sh: add --yes/-y flag for non-interactive mode
- seed-from-prod.sh: delete editorial_pipeline_settings before import to avoid duplicate key error
- Justfile dev-reset: auto-detect PRD_DB_URL from packages/web/.env.local
- Justfile dev-reset: graceful fallback when decoded-backend network is absent

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(web): resolve pre-existing lint errors (prettier + eslint)

Auto-fix prettier formatting across admin pages and add eslint-disable
for untyped Supabase entity table access. Attach cause to firecrawl
timeout error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore(web): fix prettier format + add .next.bak to prettierignore

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(api-server): resolve pre-existing clippy errors

- Remove duplicate has_url fields in solutions/tests.rs
- Fix unnecessary cast in gemini_cost.rs
- Allow clippy::disallowed_methods for json!() macro in content_studio

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(api-server): resolve remaining clippy errors for pre-push hook

- Allow disallowed_methods for json!() macro in raw_posts handlers
- Use next_back() instead of last() on DoubleEndedIterator
- Fix doc_lazy_continuation in solutions/spots service
- Allow too_many_arguments for create_solution_for_verify
- Allow type_complexity for test-only InMemoryStorage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(api-server): allow too_many_arguments for create_post_from_raw

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(api-server): resolve final clippy errors (println + too_many_arguments)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(api-server): skip dotenvy in test mode to prevent .env.dev interference

Config tests that verify default values were failing because
dotenvy reloaded .env.dev inside from_env(), overriding remove_var calls.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(api-server): ignore RUSTSEC-2026-0104 for AWS SDK legacy TLS chain

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…511)

#509 의 verify-stats 섹션이 SQL 오류로 실패. ARRAY["..."] 는 PostgreSQL
에서 identifier (column) 로 해석되어 ARRAY['...']::uuid[] 가 정답.

jq 의 join 구분자도 "," → ',' 로 변경. counts_json 빈 문자열 fallback 추가.
…via cherry-pick + #509 lineage)

main 의 a7463c0 (backend bump), d95fbda (#511) 은 직전 cherry-pick (9a88c56, 8f37d04) 으로 dev 에 동일 내용 반영. main 의 #509 release 가 가져갔던 모든 dev 변경 (#498, #499 등) 은 dev origin 에 그대로 보존. tree 동일성은 'ours' strategy 로 보존하고 commit graph 에서 main 을 parent 로 기록해 merge-base 를 advance — 다음 dev → main 머지를 fast-forward 가능하게 만든다.
CRITICAL:
- vault-sync.yml 스크립트 인젝션 수정 (${{ client_payload }} → env: 바인딩)

HIGH:
- Secret 배치 통합 (DOCS_DEPLOY_KEY 제거, VAULT_DEPLOY_KEY + DOCS_DISPATCH_TOKEN 2개로 정리)
- 마이그레이션 스크립트 안전성 (find -print0, mv -n, dry-run 지원)
- 개인 vault 폴더 수 + CLAUDE.md drift 명시
- Track 하네스 삭제/검증 순서 역전 수정 (병행 → 확인 → 삭제)
- MCP 경로 틸드 → 절대 경로
- 리서치 문서 참조 TODO 표기 (3종 모두)

MEDIUM:
- workflow_dispatch inputs 추가 (수동 실행 빈 항목 방지)
- concurrency + git pull --rebase (race condition 방지)
- obsidian-git autoCommitMessage, disablePush, tracking branch 추가
- vault-dispatch.yml set -eo pipefail, PR merge/close 구분
- Telegram systemd 하드닝 (NoNewPrivileges, ProtectSystem)
- Redaction 패턴 확장 (Anthropic, AWS, fine-grained PAT)
- Push 주기 Phase 1/2 구분 명시
- Webhook 전환 로드맵 + secret_token
- Kill-switch 환경변수 추가
- assets/ 폴더 팀 vault에 추가
- .gitignore + 플러그인 인식 가이드 설명

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
리서치 원문을 §1~§11 구조로 변환하여 docs/research/에 배치.
스펙 3종의 TODO 마커를 실제 링크로 교체하여 참조 체인 완성.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
thxforall and others added 7 commits May 14, 2026 19:17
- Plan 1: Team vault infrastructure (decoded-docs repo + GitHub Actions)
- Plan 2: Personal vault claude_volt (폴더 구조 + 템플릿 + MCP)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add try-on ux web funnel design

* docs: add try-on ux implementation plan

* docs: refine try-on ux implementation plan

* feat: add vton photo consent gate

* fix: tighten vton photo consent sheet

* feat: add vton mobile photo inputs

* fix: harden vton photo input flow

* feat: polish vton item search

* fix: harden vton item search

* fix: type vton photo consent parsing

* feat: add profile closet try-on entry

* fix: harden profile closet tab

* feat: track vton result sharing

* test: cover vton result tracking

* fix: harden vton closet and sharing flow

* feat: enhance vton search sharing and photo editing

* feat: refine vton photo consent controls

* fix: route post try cta through vton modal

* feat: persist vton photo consent
decoded-agent / decoded-docs / decoded-monorepo 알림이 같은 채팅에 모이므로
[repo] prefix로 식별 가능하게 통일.
)

decoded-agent / decoded-docs / decoded-monorepo 알림이 같은 채팅에 모이므로
[repo] prefix로 식별 가능하게 통일.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 15, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
decoded-app Ready Ready Preview, Comment May 16, 2026 9:13am

thxforall and others added 4 commits May 15, 2026 16:45
- tags.md: 도구 카테고리에 obsidian, telegram 추가
- 스펙 3종: infra→ops, docs/personal 제거 (어휘 외 태그 사용 금지)
- wiki:lint 0 errors 확인
- CLAUDE.md: add 'Documentation 위치' table — code/LLM-routing stays here,
  business/planning/meetings/decisions/architecture lives in decoded-docs.
  Link to sync-policy.md as single source of truth.
- .planning/NEXT-GEN-MAGAZINE.md, .planning/vertical-layers-strategy.md:
  replace with redirect stubs pointing to vault.

Migrated content lives at:
  https://github.com/decodedcorp/decoded-docs/blob/main/Project/
…-spec

feat(docs): Obsidian vault 전환 — 스펙/플랜/workflow
docs(routing): point planning content at decoded-docs vault
홈 히어로 카드 클릭 시 모달처럼 가운데 확대만 되고 끝나던 문제를 해결한다.

- focus 상태에서 카드 하단 바깥에 "View editorial" CTA 표시 (카드 회전을 상쇄해 항상 수평 유지). pointerdown 발생 위치를 ref로 기억해 GSAP Draggable의 onClick 콜백에서 navigate 분기 — React stopPropagation은 native bubble 단계보다 늦게 발화해 안전하지 않음.
- MainHero가 마운트 시점의 sessionStorage(`hero-cover-seen`) 값을 기억해 skipEntry로 내려준다. 디테일에서 돌아온 같은 세션에서는 burst 진입 애니메이션을 건너뛰고 카드를 즉시 제자리에 두어 "셔플" 인상을 제거한다.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@cocoyoon cocoyoon added the bump:minor Backward-compatible additions label May 16, 2026
@cocoyoon cocoyoon merged commit 4577402 into main May 16, 2026
11 of 12 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in decoded-monorepo May 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bump:minor Backward-compatible additions

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants