From 856e375f3e672172a11309f594b2f1148d476c76 Mon Sep 17 00:00:00 2001 From: Yun Chan Date: Wed, 23 Sep 2026 16:39:48 +0900 Subject: [PATCH] docs(release): record the v1.5.0 publish and download refresh Notes the canonical feed now serving 1.5.0 (installer hash verified), the runtime and portable aliases pointing at the same version, and the landing site reporting commit 5c11ee2 / 1.5.0. Also records that the site's CI deployment still fails without a Cloudflare API token, so this refresh went out through an authenticated local deploy, and that the stale-alias bug found while publishing is fixed and closed. --- bucket/d3ro-voice.json | 8 ++++---- docs/deployment/release-guide.md | 24 ++++++++++++++++++++---- docs/map/00-index.md | 2 +- docs/map/11-gap-backlog.md | 9 +++++---- memory/project_status.md | 10 ++++++++++ 5 files changed, 40 insertions(+), 13 deletions(-) diff --git a/bucket/d3ro-voice.json b/bucket/d3ro-voice.json index 6a68196..115b1ed 100644 --- a/bucket/d3ro-voice.json +++ b/bucket/d3ro-voice.json @@ -1,15 +1,15 @@ { - "version": "1.3.5", + "version": "1.5.0", "description": "로컬 AI 음성 어시스턴트 (faster-whisper + Ollama, 100% 오프라인 지원)", "homepage": "https://d3ro.chanpaca.net", "license": "MIT", "architecture": { "64bit": { "url": [ - "https://git.chanpaca.net/api/packages/yunchan/generic/d3ro-voice/portable-1.3.5/D3RO-Voice-1.3.5-x64-portable.7z.001" + "https://git.chanpaca.net/api/packages/yunchan/generic/d3ro-voice/portable-1.5.0/D3RO-Voice-1.5.0-x64-portable.7z.001" ], "hash": [ - "45ecac82d2665cd64dd4a6edcfed1fa7874dce7116608664e0ba795a1c1509e7" + "7e028887e1f2f9fc1204b53256e909fd3eda5a356ed86362c34b80424bf88c90" ] } }, @@ -27,7 +27,7 @@ "architecture": { "64bit": { "url": [ - "https://git.chanpaca.net/api/packages/yunchan/generic/d3ro-voice/portable-$version/D3RO-Voice-1.3.5-x64-portable.7z.001" + "https://git.chanpaca.net/api/packages/yunchan/generic/d3ro-voice/portable-$version/D3RO-Voice-1.5.0-x64-portable.7z.001" ] } } diff --git a/docs/deployment/release-guide.md b/docs/deployment/release-guide.md index 103a320..823d5fe 100644 --- a/docs/deployment/release-guide.md +++ b/docs/deployment/release-guide.md @@ -6,7 +6,7 @@ | 항목 | 정본 | 현재 판정 | |---|---|---| -| 제품 버전 | `release/product-version.json`: `1.3.0` | source SSOT 확정 | +| 제품 버전 | `release/product-version.json`: `1.5.0` | source SSOT 확정 | | Android | versionCode `1030001` | production AAB 미생성 | | iOS | build `1030001` | production archive 미검증 | | Android upload key | alias `d3ro-upload-20260821`, cert SHA-256 `4F:AC:69:24:...:15:2B:54` | external PKCS12·user-only ACL·Credential Manager·private-key readback GREEN; CI secret·복구 백업·AAB signer 대조 대기 | @@ -16,10 +16,10 @@ | Firebase | Console `u/0`, `u/1` 모두 D3RO project 없음 | 사용자 승인 후 project·Android app 생성 필요 | | AdMob | app `ca-app-pub-1039714767792854~6427959892`; banner `/9840591290`; rewarded `/2255790918` | SSOT 확정. `검토 필요`·`광고 게재 제한`·store 미연결·결제 프로필 미완료 | | updater feed (canonical) | `https://git.chanpaca.net/api/packages/yunchan/generic/d3ro-voice/latest` | Forgejo Generic Registry. GitLab project 1172은 legacy mirror | -| release notes | `CHANGELOG.md` `## [1.3.0]` + Play changelog `1030001.txt` (ko/en) | 태그 전 확정·검증 필수 | -| 직전 게시본 | Forgejo Release `v1.1.0` (2026-09-15 게시, unsigned installer) | `v1.2.0`·`v1.3.0` 파이프라인은 Forgejo 저장소 시크릿이 없어 실패 = 게시본 없음. 설치본이 있는 마지막 버전은 `1.1.0` | +| release notes | `CHANGELOG.md` `## [1.5.0]` + Play changelog `1050000.txt` (ko/en) | 태그 전 확정·검증 필수 | +| 직전 게시본 | Forgejo Generic Registry `1.5.0` (2026-09-23 게시, unsigned installer + runtime/portable) | `v1.2.0`·`v1.3.0` 파이프라인은 서명/사이드카 선행 조건에서 실패했고 `v1.4.0`은 게시되지 않았다. `1.3.2`·`1.3.7`·`1.5.0`은 무서명 로컬 게시 경로(GAP-REL-06)를 사용했다 | -live canonical feed(`git.chanpaca.net/.../d3ro-voice/latest`)의 `latest.yml`은 현재 `1.1.0`을 보고한다. 시크릿을 채우고 `v1.3.0` 파이프라인이 GREEN이 되면 그 값이 `1.3.0`으로 올라가고, 그때부터 기존 설치본이 자동 업데이트를 받는다. +live canonical feed(`git.chanpaca.net/.../d3ro-voice/latest`)의 `latest.yml`은 현재 `1.5.0`을 보고한다(2026-09-23 무서명 로컬 게시). `runtime-latest/runtime.json`과 `portable-latest/portable.json`도 `1.5.0`이다. 서명 인증서를 확보하면 이보다 높은 버전을 서명 게시해 대체한다. ## desktop 릴리스 파이프라인 @@ -110,6 +110,22 @@ https://gitlab.twentyoz.kr:8443/api/v4/projects/1172/packages/generic/d3ro-voice - **회수(rollback)**: `stagingPercentage`를 낮추거나 `killSwitch`를 켠다. 이미 배포된 버전은 되돌리지 않고 더 높은 patch로 forward-fix한다. +## `1.5.0` 릴리스 기록 (2026-09-23) + +서명 인증서가 없는 상태라 CI 서명 파이프라인 대신 **무서명 로컬 게시**로 내보냈다. + +1. `release/product-version.json`을 `1.5.0`(android/iOS `1050000`, 2026-09-23)으로 올리고 `npm run version:sync` → `npm run version:check` GREEN. +2. `CHANGELOG.md` `## [1.5.0] - 2026-09-23` 확정. `site/src/release.ts`와 `apps/web/src/lib/desktop-release.ts`는 `sync-version`이 자동 반영. +3. 게이트 GREEN: `version:check`, `release:metadata`, `security:secrets`, `check:design`, `lint`, `typecheck`, 데스크톱 빌드, `check:desktop-renderer`. +4. 커밋 후 `npm run release:tag` → annotated `v1.5.0` → `git push chanpaca main` + `git push chanpaca v1.5.0` (legacy mirror `origin`에도 함께). +5. 런타임을 먼저 게시: `npm run sidecar:build`(UIA 포함) → `npm run release:portable:build` → `npm run release:portable`. `runtime-latest/runtime.json`과 `portable-latest/portable.json`이 `1.5.0`을 보고하고, 로컬 해시와 원격 sha256이 일치함을 확인했다. +6. 업데이터 게시: `npm run release:updater -- --build --ack-unsigned`. 설치본 `D3RO-Voice-Setup-1.5.0-x64.exe` = 91.2MiB(95,612,108 bytes), `latest.yml` 버전 `1.5.0`, sha512가 로컬과 일치하며 canonical feed에서 익명 206을 반환한다. +7. 사이트: CI `deploy` 워크플로는 `CF_API_TOKEN` 시크릿이 없어 여전히 실패한다(GAP-REL-09b). 대신 인증된 로컬 wrangler로 `npm run build --prefix site` → release-identity 작성 → `npx wrangler pages deploy site/dist --project-name d3ro --branch main`을 실행했고, `https://d3ro.chanpaca.net/release-identity.json`이 commit `5c11ee2`, version `1.5.0`을 보고한다. 라이브 번들이 `D3RO-Voice-Setup-1.5.0-x64.exe`를 계산한다. +8. 게시 중 발견: `publish-portable-release.mjs`의 "동일 파일 건너뛰기"가 1MiB 초과 파일을 크기만으로 비교해, 크기가 우연히 같은 `runtime-latest` 볼륨 일부를 낡은 바이트로 남겼다. 파일 목록 API의 sha256으로 정확히 비교하고, 내용이 다르면 별칭 버전 전체를 지우고 다시 올리도록 수정했다(GAP-REL-11). +9. **남은 게이트**: 실제 설치본에서 자동 업데이트 E2E, Electron GUI에서 제안 오버레이·수락 삽입·비밀번호 차단 검증(GAP-INPUT-07), 모바일 production은 별개. + + + ## `1.3.0` 릴리스 절차 (canonical = Forgejo Actions) **선행 조건 — Forgejo 저장소 시크릿.** `.forgejo/workflows/release.yml`은 아래 4개가 diff --git a/docs/map/00-index.md b/docs/map/00-index.md index ab70016..a896c26 100644 --- a/docs/map/00-index.md +++ b/docs/map/00-index.md @@ -2,7 +2,7 @@ > Status: ACTIVE > Last full audit: 2026-09-13 -> Last update: 2026-09-23 — **v1.5.0 릴리스.** CHANGELOG `[1.5.0]` 을 확정하고 버전 SSOT를 1.5.0(android/iOS 1050000)으로 올렸으며, `site/src/release.ts` 와 `apps/web/src/lib/desktop-release.ts` 다운로드 링크를 1.5.0으로 동기화했다. 이 릴리스는 입력 인텔리전스(`INPUT-01`~`INPUT-18`, 전부 데스크톱 `[~]`), 커스텀 인스트럭션 수정(AI-04/05), `LocalLLMService` 요청별 취소·상한을 포함한다. 인증서가 없어 무서명 업데이터 게시 예외(GAP-REL-06)를 유지한다. +> Last update: 2026-09-23 — **v1.5.0 릴리스.** CHANGELOG `[1.5.0]` 을 확정하고 버전 SSOT를 1.5.0(android/iOS 1050000)으로 올렸으며, `site/src/release.ts` 와 `apps/web/src/lib/desktop-release.ts` 다운로드 링크를 1.5.0으로 동기화했다. 이 릴리스는 입력 인텔리전스(`INPUT-01`~`INPUT-18`, 전부 데스크톱 `[~]`), 커스텀 인스트럭션 수정(AI-04/05), `LocalLLMService` 요청별 취소·상한을 포함한다. 인증서가 없어 무서명 업데이터 게시 예외(GAP-REL-06)를 유지한다. 게시 결과: canonical feed `latest.yml`=1.5.0(설치본 91.2MiB / 95,612,108 bytes, sha512 일치, 익명 206), `runtime-latest`/`portable-latest`=1.5.0(원격 sha256 검증), 사이트 `https://d3ro.chanpaca.net/release-identity.json`=commit `5c11ee2`/1.5.0. 게시 중 portable 별칭이 낡은 바이트와 새 바이트로 갈라지는 버그(GAP-REL-12)를 발견해 게시 스크립트를 수정했다. > > Previous update: 2026-09-22 — **Gemma/Ollama 폭주 방어 계약을 기록했다.** 19:14:12 부팅 워밍업이 `keep_alive: 30m`으로 `gemma4:e4b`를 19:44:12까지 VRAM 3,226,342,521 bytes / context 4096으로 강제 상주시킨 것이 관측됐으며, 같은 시점 Windows GPU Engine PID 표본에는 Ollama의 활성 compute가 없었다. 즉 당시 상태는 무한 추론이 아니라 강제 residency였다. 19:11:17~19:11:58의 자동 제안 연속 생성은 기존 900 ms·12/min·5 candidates·128 tokens·한 글자 재생성 정책이 허용한 burst였다. 현재 구현 계약은 부팅 warmup 제거, `keep_alive: 2m`, 제안 600 ms / 최소 5 s 간격 / 기본 6회·hard max 12회 per min / 3 candidates / 64 tokens / 12-char growth / 8 s timeout, 그리고 요청별 취소·상한·종료 정리다. 독립 표적 검증은 6 test files / 69 tests passed / 0 failed, 변경 코드·테스트 ESLint와 `git diff --check`도 exit 0이다. Raw Ollama에서는 cold bounded 요청이 client hard timeout 15.044 s에 취소된 뒤 `/api/ps`가 비었고 `/api/version`은 80 ms에 회복했다. 명시 warmup은 HTTP 200 / 16.639 s, 후속 warm 요청은 body `options.num_predict=1`, `keep_alive='2m'`로 553 ms HTTP 200 / `done:true` / `eval_count:1` / `done_reason:length`였고 `/api/ps` expiry는 약 119.9 s였다. **19:48:59 +09:00에는 새 generate/unload/kill/retry 없이 충분히 지난 뒤 한 번의 `/api/ps`가 HTTP 200 / 45.8 ms / `{models:[]}`였고 `/api/version`은 HTTP 200 / 7.3 ms / `0.32.13`이었다.** 이는 raw API 수준의 expiry 뒤 unload 확인일 뿐 앱 재시작·GUI·실제 타이핑 증거는 아니므로 상태는 `[~]`로 유지한다 (`11` GAP-LLM-04, GAP-INPUT-06). > diff --git a/docs/map/11-gap-backlog.md b/docs/map/11-gap-backlog.md index 776f399..06a9ebe 100644 --- a/docs/map/11-gap-backlog.md +++ b/docs/map/11-gap-backlog.md @@ -22,14 +22,15 @@ Legend: `[ ]` open · `[~]` in progress · `[!]` blocked externally · `[x]` res | ID | Area | Gap | Evidence | Suggested next step | |---|---|---|---|---| | GAP-QA-01 | Quality | Extreme Red Team: headful end-to-end bug hunting across real desktop Electron, Web Next.js, and CI pipelines. | `red_team_log.md`, `tests/e2e/red_team_cycle*.spec.ts`, `apps/web/e2e/red_team_cycle4_web.spec.ts` | `[x]` 2026-09-15: 18 scenarios executed, 14 defects caught and 100% resolved (infinite chunking loop DEF-008, IPC signature mismatch DEF-004, markdown editor typing rollback DEF-006, Web RSC Link serialization DEF-012, secret scanner lookahead DEF-013, etc.). All 18 scenarios GREEN with zero regressions. | -| GAP-REL-01 | Release | Official release publication to Forgejo and active public download center deployment. | `scripts/ci/publish-forgejo-release.mjs`, `apps/web/src/app/download/page.tsx`, `site/src/sections/Download.tsx`, `apps/web/e2e/red_team_cycle4_web.spec.ts` | `[~]` 2026-09-15: v1.1.0 release assets (`D3RO-Voice-Setup-1.1.0-x64.exe`, `.blockmap`, `latest.yml`, `update-policy.json`) published to canonical Forgejo registry and release hub. 2026-09-16: the published 1.1.0 installer carries no Authenticode signature, so it does not satisfy the release policy; product version moved to `1.2.0` and publication must come from CI with the signing gate GREEN. Download centers in `apps/web` (`/download`) and `site` (`#download`) link the canonical Forgejo feed. 2026-09-19: `1.3.7` (overlay fix) is published to the canonical updater feed (`latest.yml` = 1.3.7, 90.6MiB); the CI signing gate that blocks tag-driven publication is still unresolved, so this went out through the local updater path (GAP-REL-06). | +| GAP-REL-01 | Release | Official release publication to Forgejo and active public download center deployment. | `scripts/ci/publish-forgejo-release.mjs`, `apps/web/src/app/download/page.tsx`, `site/src/sections/Download.tsx`, `apps/web/e2e/red_team_cycle4_web.spec.ts` | `[~]` 2026-09-15: v1.1.0 release assets (`D3RO-Voice-Setup-1.1.0-x64.exe`, `.blockmap`, `latest.yml`, `update-policy.json`) published to canonical Forgejo registry and release hub. 2026-09-16: the published 1.1.0 installer carries no Authenticode signature, so it does not satisfy the release policy; product version moved to `1.2.0` and publication must come from CI with the signing gate GREEN. Download centers in `apps/web` (`/download`) and `site` (`#download`) link the canonical Forgejo feed. 2026-09-19: `1.3.7` (overlay fix) is published to the canonical updater feed (`latest.yml` = 1.3.7, 90.6MiB); the CI signing gate that blocks tag-driven publication is still unresolved, so this went out through the local updater path (GAP-REL-06). 2026-09-23: `1.5.0` published the same way (updater feed `latest.yml` = 1.5.0, installer 91.2MiB / 95,612,108 bytes, sha512 matches the local build and the feed serves it anonymously); the runtime and portable channels were republished first. | | GAP-REL-08 | Release | 다운로드 센터가 **존재하지 않는 설치 파일**을 가리켰다. `apps/web/src/lib/desktop-release.ts`와 `site/src/release.ts`의 `DESKTOP_VERSION`이 `1.2.0`에 멈춰 있어 설치 URL이 `D3RO-Voice-Setup-1.2.0-x64.exe`였고, 그 경로는 피드에서 404다(실측: 1.2.0=404, 1.3.7=206). `version:sync`가 이 두 표면을 덮지 않아 계속 어긋났다. | `scripts/ci/sync-version.mjs`, `apps/web/src/lib/desktop-release.ts`, `site/src/release.ts` | `[x]` 2026-09-19: 두 다운로드 계약 파일을 `sync-version.mjs` 대상에 추가해 버전·릴리스일이 SSOT에서 자동 반영되도록 하고, 현재 값(1.3.7 / 2026-09-19)으로 정정했다. `version:check`·typecheck·site 빌드 GREEN. | | GAP-REL-09 | Release | 랜딩 사이트가 **재배포되지 않는다**. `deploy` 워크플로가 main push마다 실패한다. 실측 원인(run#66 로그): `site/src/sections/Hero.tsx`가 타이머 ref를 `NodeJS.Timeout`으로 타이핑해 `@types/node` 네임스페이스가 필요했고, 배포 잡은 `npm ci --prefix site`만 하므로 조상 `node_modules`의 hoisted 타입이 없어 `tsc -b`가 `TS2503: Cannot find namespace 'NodeJS'`로 실패한다. 그래서 `https://d3ro.chanpaca.net/release-identity.json`이 404다(공개 버전 검증 불가). | `.forgejo/workflows/deploy-site.yml`, `site/src/sections/Hero.tsx` | `[x]` 2026-09-19: ref를 `ReturnType`으로 바꿔 hoisted 타입 의존을 제거했다(격리 `--typeRoots`로 CI 조건 재현 → 수정 전 TS2503, 수정 후 clean). 같은 수정을 push하자 `deploy` run#67이 사이트 빌드를 통과해 `dist/`를 만들었고, 실패는 다음 단계(Cloudflare)로 이동했다. | -| GAP-REL-09b | Release | `d3ro.chanpaca.net`이 404였던 직접 원인: 이 Cloudflare 계정에 Pages 프로젝트 `d3ro`/`d3ro-voice`가 **존재하지 않아** 커스텀 도메인 바인딩이 없었다(빈 본문 404, `cf-ray`만 반환). Pages 커스텀 도메인은 존 DNS CNAME(`d3ro → d3ro.pages.dev`)을 요구하는데 기존 `d3ro` 레코드가 남아 있어 `CNAME record not set`으로 pending에 머물렀고, 로컬 wrangler 자격증명에는 DNS 스코프가 없다(403 Authentication error). `deploy-site.yml`은 `CF_API_TOKEN` 시크릿이 없어 마지막 게시 단계에서도 `exit 1`이다. | `server/cloudflare-site-bridge/`, `.forgejo/workflows/deploy-site.yml`, `docs/map/02-infrastructure.md` | `[x]` 2026-09-19: Pages 프로젝트 `d3ro` 생성 + `site/dist` production 배포(`d3ro.pages.dev` 200, `release-identity.json` = commit `2407f5a` / 1.3.7) + 커스텀 도메인 연결. DNS 없이 도메인을 살리기 위해 Workers 라우트 브리지(`server/cloudflare-site-bridge`, `d3ro.chanpaca.net/*` → Pages 프록시, `npx wrangler deploy`)를 배포 → 라이브 확인: `/`·`/privacy/`·`/terms/`·`/delete-account/` 200, 라이브 번들이 설치 파일명을 `1.3.7`로 계산, `/download.html` → `/#download`. 남은 정리 2건: (1) 대시보드에 CNAME을 추가한 뒤 브리지 워커 삭제, (2) CI 자동 게시를 위해 `CF_API_TOKEN`(Pages/Workers Edit) + `CF_ACCOUNT_ID`=`8e83cc130e7329c160cf2b88d6b4c20a`를 Forgejo 시크릿에 등록. | +| GAP-REL-09b | Release | `d3ro.chanpaca.net`이 404였던 직접 원인: 이 Cloudflare 계정에 Pages 프로젝트 `d3ro`/`d3ro-voice`가 **존재하지 않아** 커스텀 도메인 바인딩이 없었다(빈 본문 404, `cf-ray`만 반환). Pages 커스텀 도메인은 존 DNS CNAME(`d3ro → d3ro.pages.dev`)을 요구하는데 기존 `d3ro` 레코드가 남아 있어 `CNAME record not set`으로 pending에 머물렀고, 로컬 wrangler 자격증명에는 DNS 스코프가 없다(403 Authentication error). `deploy-site.yml`은 `CF_API_TOKEN` 시크릿이 없어 마지막 게시 단계에서도 `exit 1`이다. | `server/cloudflare-site-bridge/`, `.forgejo/workflows/deploy-site.yml`, `docs/map/02-infrastructure.md` | `[x]` 2026-09-19: Pages 프로젝트 `d3ro` 생성 + `site/dist` production 배포(`d3ro.pages.dev` 200, `release-identity.json` = commit `2407f5a` / 1.3.7) + 커스텀 도메인 연결. DNS 없이 도메인을 살리기 위해 Workers 라우트 브리지(`server/cloudflare-site-bridge`, `d3ro.chanpaca.net/*` → Pages 프록시, `npx wrangler deploy`)를 배포 → 라이브 확인: `/`·`/privacy/`·`/terms/`·`/delete-account/` 200, 라이브 번들이 설치 파일명을 `1.3.7`로 계산, `/download.html` → `/#download`. 남은 정리 2건: (1) 대시보드에 CNAME을 추가한 뒤 브리지 워커 삭제, (2) CI 자동 게시를 위해 `CF_API_TOKEN`(Pages/Workers Edit) + `CF_ACCOUNT_ID`=`8e83cc130e7329c160cf2b88d6b4c20a`를 Forgejo 시크릿에 등록. **2026-09-23:** CI `deploy`(deploy-site.yml)는 `CF_API_TOKEN` 부재로 run#71까지 여전히 실패한다. 로컬 인증 wrangler(pages:write)로 `npm run build --prefix site` → release-identity 작성 → `npx wrangler pages deploy site/dist --project-name d3ro --branch main`을 수동 배포했고, 라이브 `https://d3ro.chanpaca.net/release-identity.json`이 commit `5c11ee2` / version `1.5.0`을, 라이브 번들이 설치 파일명 `D3RO-Voice-Setup-1.5.0-x64.exe`를 보고한다. 자동 게시에는 여전히 시크릿 등록이 필요하다. | | GAP-REL-10 | Release | `release-windows`(태그 파이프라인)는 서명 가드에 도달하기 **전에** sidecar 단계에서 죽는다. 이 러너 컨텍스트에서는 `sidecar:setup`이 Python 3.11+를 찾지 못한다(`Python 3.11+ 를 찾을 수 없습니다`) → `sidecar:build` → `verify-sidecar-bundle.mjs` 연쇄 실패(실측: run#65 `v1.3.7`, run#61 `v1.3.6`). 같은 러너의 portable 잡은 `py -3.11 → Python 3.11.9`를 찾아 사이드카 빌드에 성공하므로, 워크플로/컨테이너 간 PATH 차이다. | `.forgejo/workflows/release.yml`, `apps/desktop/scripts/setup-sidecar.mjs` | `[!]` 2026-09-19: 러너에 Python 3.11+(`py` 런처 포함)를 보장하거나 워크플로에 `actions/setup-python` 단계를 추가한다. 그 전까지 서명 게시는 불가능하다(GAP-REL-02와 별개 선행 차단). | | GAP-REL-11 | Release | portable 워크플로의 마지막 `actions/upload-artifact@v4` 단계가 Forgejo 러너에서 `GHESNotSupportedError`로 실패한다(증거 보존만 실패, 게시는 성공). | `.forgejo/workflows/portable.yml` | `[x]` 2026-09-19: `v1.3.7` portable 게시는 run#64에서 성공(7z 단일 볼륨 83.7MB + zip 2부, `portable-latest/portable.json`이 1.3.7 보고). 남은 조치: upload-artifact 단계를 제거하거나 v3/다른 보존 방식으로 바꿔 워크플로를 GREEN으로 만든다. | -| GAP-REL-02 | Release | Windows stable publication needs an external public-trust Authenticode PFX, its password, the exact signer subject, and a Forgejo token, none of which live in the repository. | `.forgejo/workflows/release.yml`, `.gitlab-ci.yml`, `scripts/ci/set-forgejo-secrets.mjs`, `scripts/ci/verify-windows-release-artifact.ps1` | `[!]` 2026-09-18 measured: the Forgejo repo had **zero** Actions secrets; `FORGEJO_TOKEN` is registered now (2026-09-18) but `WIN_CSC_*` still have no values, so `v1.2.0` (run 49) and `v1.3.0` (run 51) both failed at the signing guard and **no updater-feed release has been published since `1.1.0`**. Inject the four secrets (`WIN_CSC_LINK`, `WIN_CSC_KEY_PASSWORD`, `WIN_CSC_EXPECTED_SIGNER_SUBJECT`, `FORGEJO_TOKEN`) with `npm run release:secrets` (check: `npm run release:secrets:check`), then re-run `release.yml` for the `v1.3.0` tag via `workflow_dispatch` (tags are immutable). **2026-09-19 정정**: CI 서명 게이트는 여전히 막혀 있지만, updater feed에는 `1.3.2`~`1.3.7`이 로컬 `release:updater` 경로로 게시되어 있다(GAP-REL-06). | -| GAP-REL-06 | Release | 서명 인증서가 없어 stable(`latest`) 채널에 **무서명** 설치본을 게시했다. electron-updater는 `app-update.yml`에 `publisherName`이 없으면 서명 검증을 건너뛰므로 설치는 동작하지만, SmartScreen 평판은 버전마다 0부터 시작한다. | `scripts/ci/publish-updater-release.mjs`, `release/update-policy.json`, `.forgejo/workflows/release.yml` | `[!]` 2026-09-18: `1.3.2`를 `--ack-unsigned`(명시적 승인 플래그)로 게시. **2026-09-19: `1.3.7`도 같은 경로로 게시**(`npm run release:updater -- --ack-unsigned`, 설치본 90.6MiB, `latest.yml`=1.3.7, 설치본 sha512가 피드 메타데이터와 일치). 인증서 확보 시 더 높은 버전으로 서명 게시하여 대체하고, 이 예외를 제거한다. | +| GAP-REL-12 | Release | Forgejo generic registry는 파일 단위 덮어쓰기를 거부(HTTP 409)하는데, portable 게시 스크립트의 "동일 파일 건너뛰기"가 1MiB 초과 파일을 **크기만** 비교했다. 그래서 새 runtime의 sidecar 볼륨 `.001`이 이전 볼륨과 정확히 같은 94,371,840 bytes였고, 내용이 다른데도 "동일"로 오판해 `runtime-latest`가 낡은 `.001` + 새 `.002`로 갈라졌다(다운로드 시 손상). | `scripts/ci/publish-portable-release.mjs` | `[x]` 2026-09-23: 파일 목록 API(`GET /api/v1/packages/{owner}/generic/{name}/{version}/files`)의 sha256으로 정확히 비교하고, 내용이 다른 파일이 하나라도 있으면 해당 별칭 버전 전체를 삭제 후 재업로드하도록 수정했다. 이미 발생한 `runtime-latest`는 버전을 지우고 재게시해 4개 파일 모두 로컬 해시와 일치함을 확인했다. | +| GAP-REL-02 | Release | Windows stable publication needs an external public-trust Authenticode PFX, its password, the exact signer subject, and a Forgejo token, none of which live in the repository. | `.forgejo/workflows/release.yml`, `.gitlab-ci.yml`, `scripts/ci/set-forgejo-secrets.mjs`, `scripts/ci/verify-windows-release-artifact.ps1` | `[!]` 2026-09-18 measured: the Forgejo repo had **zero** Actions secrets; `FORGEJO_TOKEN` is registered now (2026-09-18) but `WIN_CSC_*` still have no values, so `v1.2.0` (run 49) and `v1.3.0` (run 51) both failed at the signing guard and **no updater-feed release has been published since `1.1.0`**. Inject the four secrets (`WIN_CSC_LINK`, `WIN_CSC_KEY_PASSWORD`, `WIN_CSC_EXPECTED_SIGNER_SUBJECT`, `FORGEJO_TOKEN`) with `npm run release:secrets` (check: `npm run release:secrets:check`), then re-run `release.yml` for the `v1.3.0` tag via `workflow_dispatch` (tags are immutable). **2026-09-19 정정**: CI 서명 게이트는 여전히 막혀 있지만, updater feed에는 `1.3.2`~`1.3.7`이 로컬 `release:updater` 경로로 게시되어 있다(GAP-REL-06). **2026-09-23 정정**: `1.4.0`은 게시되지 않았고(GAP-REL-06), `1.5.0`도 로컬 무서명 경로로 게시했다. CI 서명 게이트는 여전히 `WIN_CSC_*` 부재로 막혀 있다(GAP-REL-10의 Python 선행 문제와 별개). | +| GAP-REL-06 | Release | 서명 인증서가 없어 stable(`latest`) 채널에 **무서명** 설치본을 게시했다. electron-updater는 `app-update.yml`에 `publisherName`이 없으면 서명 검증을 건너뛰므로 설치는 동작하지만, SmartScreen 평판은 버전마다 0부터 시작한다. | `scripts/ci/publish-updater-release.mjs`, `release/update-policy.json`, `.forgejo/workflows/release.yml` | `[!]` 2026-09-18: `1.3.2`를 `--ack-unsigned`(명시적 승인 플래그)로 게시. **2026-09-19: `1.3.7`도 같은 경로로 게시**(`npm run release:updater -- --ack-unsigned`, 설치본 90.6MiB, `latest.yml`=1.3.7, 설치본 sha512가 피드 메타데이터와 일치). 인증서 확보 시 더 높은 버전으로 서명 게시하여 대체하고, 이 예외를 제거한다. **2026-09-23: `1.5.0`도 같은 경로로 게시**(`npm run release:updater -- --build --ack-unsigned`, 설치본 91.2MiB, `latest.yml`=1.5.0, sha512 검증). | | GAP-REL-07 | Release | 패키징된 `better-sqlite3`가 호스트 Node ABI여서 `1.3.2` 설치본이 시작 즉시 죽었다(NODE_MODULE_VERSION 131 vs 130). 원인: 로컬 `npm install`이 네이티브 모듈을 Node용으로 재빌드했고 패키징이 재빌드를 건너었다. | `scripts/ci/verify-native-abi.mjs`, `scripts/ci/fix-native-abi.mjs`, `.gitlab-ci.yml`/`.forgejo`/`.github` 패키징 단계 | `[x]` 2026-09-18: 패키징 후 Electron ABI를 검증하고, 검증된 트리에서만 설치본을 생성(`--prepackaged`)한다. `1.3.3`은 설치본에서 추출한 바이너리로 재검증 GREEN. | | GAP-ADS-01 | Ads | 9 of 10 desktop ad adapters still extend `UnavailableAdAdapter` (`provider_not_integrated`). | `apps/desktop/src/main/services/ads/*` | `[~]` 2026-09-13: `DirectHouseSponsorAdapter` is now a real configurable REST adapter (bid/impression/click/reward via `endpointUrl`; fail-closed when unconfigured; 22 unit tests GREEN). Remaining 9 need official SDKs/authenticated endpoints. | | GAP-ADS-02 | Ads | Desktop mediation reward accounting is not wired to license quota (`claimReward` still returns no tokens). | `AdMediationEngine.ts`, `AppLayout.tsx` | Wire verified `reportRewardCompletion` to `LicenseService` quota after the direct sponsor endpoint exists. | diff --git a/memory/project_status.md b/memory/project_status.md index 48b6cb3..a071ce6 100644 --- a/memory/project_status.md +++ b/memory/project_status.md @@ -1,5 +1,15 @@ # D3RO-VOICE 프로젝트 현황 +## v1.5.0 릴리스 완료 (2026-09-23) 🚀 + +- 버전 SSOT `1.5.0`(android/iOS `1050000`, releaseDate 2026-09-23). annotated 태그 `v1.5.0` 생성 후 `chanpaca`(canonical)와 `origin`(legacy mirror) main+tag push 완료. +- 게이트: `version:check` GREEN, `release:metadata` GREEN, `security:secrets` GREEN, `check:design` 0 violations, `lint` clean, `typecheck` clean, 데스크톱 빌드/`check:desktop-renderer`(7 pages) GREEN. 테스트는 native ABI 미스매치 구성에서 `366 failed | 1099 passed (1465)` — 실패 수는 기존 베이스라인과 동일(365 ABI + 1 stale sidecar 메시지, GAP-QA-02)로 신규 실패 0. +- 인증서가 없어 CI 서명 파이프라인 대신 무서명 로컬 게시: `sidecar:build`(UIA/`uiautomation`/`comtypes` 포함) → `release:portable:build` → `release:portable`(runtime-latest + portable-latest) → `release:updater --build --ack-unsigned`. +- 검증된 라이브 상태: canonical feed `latest.yml`=1.5.0(설치본 `D3RO-Voice-Setup-1.5.0-x64.exe` 95,612,108 bytes, sha512 일치, 익명 206), `runtime-latest/runtime.json`·`portable-latest/portable.json`=1.5.0(원격 sha256 = 로컬), 사이트 `d3ro.chanpaca.net/release-identity.json`=commit `5c11ee2`/1.5.0. +- 게시 중 portable 별칭(`runtime-latest` 등)의 "동일 파일 건너뛰기"가 1MiB 초과 파일을 크기만 비교해 낡은 `.001`과 새 `.002`가 섞이는 사고를 발견 → `publish-portable-release.mjs`를 파일 목록 API sha256 비교 + 별칭 버전 원자적 재게시로 수정(GAP-REL-12). +- 사이트 CI(`deploy-site.yml`)는 `CF_API_TOKEN` 시크릿 부재로 여전히 실패(GAP-REL-09b). 이번에는 인증된 로컬 wrangler로 수동 배포했다. +- 남은 검증: 실제 설치본 자동 업데이트 E2E, Electron GUI에서 제안 오버레이/수락 삽입/비밀번호 차단(GAP-INPUT-07). + ## Local Flow Intelligence 10-pack — 문서/자동 증거 handoff (2026-09-22) - 데스크톱 기존 InputTelemetry/Suggestion/UI 확장으로 Flow Radar, Edit Friction, App DNA, App Quality, Privacy Receipt, Smart Exclusion, Why This Suggestion, Memory Decay, Instant Recall, Shortcut Safety Audit을 구현했다. raw key events/key codes/content stream은 저장하지 않지만, 동의한 학습 텍스트는 `typing_samples`/`personal_phrases`에 별도 정책으로 남을 수 있다. `personal_phrases`는 나이 기반 자동 만료가 없고 개별 삭제·전체 삭제·동의 철회로 제거된다.