137 Commits

Author SHA1 Message Date
Leo 3cc8788e9d sync: collector/ 同步自 inkstone/arcrun-rag@d129f8b(桌面小幫手 0.18.48) 2026-08-28 09:16:44 +08:00
Leo e8800d18ef sync: collector/ 同步自 inkstone/arcrun-rag@1c31ac2(桌面小幫手 0.18.46) 2026-08-28 05:07:35 +08:00
Leo 80877af60c sync: collector/ 同步自 inkstone/arcrun-rag@740c55c(桌面小幫手 0.18.44) 2026-08-28 03:46:42 +08:00
Leo ef5fe1df6f sync: collector/ 同步自 inkstone/arcrun-rag@4982b50(桌面小幫手 0.18.43) 2026-08-28 01:48:25 +08:00
Leo 3d1cb83110 sync: collector/ 同步自 inkstone/arcrun-rag@7c21a3a(桌面小幫手 0.18.42) 2026-08-27 21:30:21 +08:00
Leo 6e80296362 sync: collector/ 同步自 inkstone/arcrun-rag@d761106(桌面小幫手 0.18.41) 2026-08-27 19:55:35 +08:00
Leo 58d47d9080 sync: collector/ 同步自 inkstone/arcrun-rag@85cb90d(桌面小幫手 0.18.40) 2026-08-27 14:03:50 +08:00
Leo 4d966b7922 sync: collector/ 同步自 inkstone/arcrun-rag@c6cf717(桌面小幫手 0.18.38) 2026-08-26 21:04:03 +08:00
Leo a1786135d5 sync: collector/ 同步自 inkstone/arcrun-rag@1edbbb2(桌面小幫手 0.18.37) 2026-08-26 17:22:08 +08:00
Leo d028a66625 sync: collector/ 同步自 inkstone/arcrun-rag@45b74a4(桌面小幫手 0.18.36) 2026-08-24 18:26:55 +08:00
Leo f1658fd2ad sync: collector/ 同步自 inkstone/arcrun-rag@a39cea2(桌面小幫手 0.18.34) 2026-08-20 11:26:28 +08:00
Leo 969c3f2bcf revert: 還原被第一次同步蓋掉的三個檔(D95,inkstone/InkStoneCo#40)
2026-08-18 出貨線第一次真的把 arcrun-rag 的 collector/ 同步過來,
盲目鏡射蓋掉/刪掉了三個**本來就該只屬於這個 repo** 的檔:

  .gitignore           21 行的「不要再長回來」閘 → 被換成 arcrun-rag 那份的一行
                       🔴 最嚴重:那份清單正是擋 43 MB 建置產物的東西,
                          它被蓋掉之後,下一趟同步就會把產物搬進來
  README.md            介紹這個獨立 repo 的 → 被換成 arcrun-rag 子目錄的 README
  cmd/collector/main.go 正常源碼,被誤判成建置產物刪掉
                       (.gitignore 的 `collector` 一行是指根目錄那顆執行檔,
                         但無斜線樣式在任何層級都命中 ⇒ 連 cmd/collector/ 也中)

出貨線那側已修(installer/scripts/line-source-repo.mjs):
  · destOwned:目的 repo 的門面檔不覆蓋、不刪除,且同步前檢查它們還在不在
  · 已被目的 repo 追蹤的檔案不算「被忽略」(git 自己的語意)⇒ main.go 不再被誤刪

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 21:33:34 +08:00
Leo a35421e2e2 sync: collector/ 同步自 inkstone/arcrun-rag@99cc64f(桌面小幫手 0.18.33) 2026-08-18 21:23:45 +08:00
Leo 8c22c6858b feat(daemon): 0.18.33——機器 ID 進二進位,三平台成品全裸號(inkstone/mira#6)
collector 加了 machineid.go 之後 daemon 源碼與成品脫節,
daemon-source-check 擋下(閘是對的)。照規矩補 changelog 再重編三平台。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 17:53:23 +08:00
Leo 48546a768c feat(daemon): 0.18.32 三平台全裸號成品+補上 msix 的搬運缺口(arcrun-rag#88)
- 產生端實測吐裸號:changelog 標題 `## 0.18.31`、二進位 -X main.version=0.18.31
- 三平台成品檔名全裸:Arcrun-0.18.32.dmg / Arcrun-win-0.18.32.exe / Arcrun-0.18.32.msix
- 🔴 補上一個安靜的洞:build-msix.sh 只吐到 dist-msix/Arcrun.msix,
  而出貨線找的是 dist/Arcrun-<版本>.msix,且 msix 標 required:false
  ⇒ 它會安靜地少一個檔而出貨線照印綠。上一版的 msix 是人手搬的。
  這正是 daemon-sync 當初要根治的「只活在人記憶裡的步驟」。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 16:55:59 +08:00
Leo 9299ec8960 feat(88): 版本線判別器從「有沒有 v」換成 DAEMON_LINE,產生端才吐得出裸號
leo 2026-08-17:「對外號就是三個數字,不要 v」。

08-18 第一次改失敗的理由要先講清楚:那個 `v` **兼任版本線判別器**
(daemon `v0.18.x` vs 雲端引擎 `1.4.x`),有四處在讀它。天真拿掉之後那些閘
**不報錯**,而是往下比對到更舊的 `## v0.18.29(…)` ⇒ 打包出 0.18.30 的執行檔、
manifest 卻宣稱是 v0.18.29。**靜默,21 站全綠。**

── 四處讀取點,各自靠什麼分辨(換掉之後)──────────────────────────
① `daemon-notes.DAEMON_RELEASED_RE`(daemon-in-bundle-gate 與 ship.mjs daemon-sync
   共用的那一份,第一輪已從四份拷貝收攏成一份)
   舊:`^## (v\d+\.\d+\.\d+)(` ——「有 v = daemon」
   新:`daemonReleasedReFor(repoRoot)` 由 `collector/DAEMON_LINE` 產生
       `^## (v?0\.18\.\d+)(` ——「在宣告的那條線上 = daemon」
② `daemon-in-bundle-gate.collectFacts/judge`(同上那份的使用端)
   新增一項 `daemon-line-declared` 排在**所有檢查最前面**:
   讀不出 DAEMON_LINE = 沒有判別器 ⇒ 當場斷,不給預設值。
   另記一個**只給錯誤訊息用**的 `changelogTopAny`,讓「沒中」時說得出
   「你指到的是 1.4.47,那是雲端那條線」——判斷與診斷分開。
③ `ship.mjs` daemon-sync 站:同 ①,並在丟例外時附上量到的事實。
④ `daemon-notes.changelogRelFor()`
   舊:`/^v/.test(version)` ⇒ 裸號時代會把 daemon 的 0.18.31 指去雲端的
   CHANGELOG.md(叫人去錯的檔補說明)。新:比對版本線。

⇒ 判別依據換成**宣告出來的事實**,不是外觀 ⇒ 產生端這才拿得掉那個 `v`:
   `daemon-version.py` 升版路徑 `"v%d.%d.%d"` → `"%d.%d.%d"`。
   「重打同一版」路徑刻意**不動**(沿用 prev_prefix):selfupdate.go 的
   newerThanCurrent() 用字串不等於判斷更新,重打時換寫法會讓每一台已安裝的機器
   看到一個內容一模一樣、卻宣稱是新版的假更新。

── 測試(+7 支,每支都附反向對照)──────────────────────────────────
故意製造「版本線判別會出錯」的情境,並證明**新的會擋、舊的會靜默放行**:
  · 產生端吐裸號 → 閘讀到最新那一版(反向對照:舊判別式讀到更舊的一版)
  · 源碼樹 0.18.31/bundle 停在 v0.18.30 → 擋(舊判別式在此判「相符」⇒ 放行)
  · DAEMON_LINE 換 0.19 而 changelog 還是 0.18.x → 擋(舊做法看不出線換了)
  · DAEMON_LINE 缺席/寫壞 → 擋,且擋在第一項
只有「新的怎麼跑」而沒有「舊的怎麼跑」的話,這些測試就只是回音,不是閘
(wiki/mistakes.md「測試複製了實作邏輯就不再是閘」)。

installer 262/262(+5)、collector daemon-version 11/11(+2)。

inkstone/arcrun-rag#88
2026-08-18 16:33:27 +08:00
Leo 681f9a10c8 WIP(88): 產生端裸號——agent 被 API 中斷前的進度,總管代存
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 16:14:20 +08:00
Leo 03a2c3d5f8 fix(ship): 解開出貨線的兩個自鎖——閘改成量指紋、戳版順序倒過來(arcrun-rag#88)
出貨線從第 5 站 daemon-source-check 就把自己鎖死,push/deploy/verify/
release-record 四站一次都沒執行過。實查真兇是**兩個**,不是一個,而且同源:
D95 第一輪把 `CHANGELOG.md` 搬進 `collector/`(為了讓 collector/ 自足、
有資格獨立成 repo)之後,**「宣告新版本」這個動作本身就是在改 collector/**。

── 自鎖① 出貨線那道閘:用 git 歷史當代理 ────────────────────────────
舊判法=`git log <宣告那顆>..HEAD -- collector` 非空就擋。
⇒ 一顆**只改宣告、一行程式都沒動**的 commit(dcd0132 就是)也被算成
  「宣告之後源碼又動過」⇒ 閘擋自己。
⇒ 反過來也不準:collector/ 底下有些檔案(打包腳本旁的 .mjs 工具、README)
  根本不會進到執行檔,動了它們一樣被判「要重打包」。

🔴 兩件事都對,疊起來變成死結:**衝突在範圍重疊,不在任何一方做錯。**

修法不是把 changelog 搬回去(那會推翻 D95 第一輪),也不是加路徑白名單
(那就是被禁的字串比對)。改成問**同一件事實**,而那份事實這個 repo 早就在算:
`daemon-version.py` 每次戳版都把「當下的原始碼指紋」記進
`.version-source.json`(2026-08-06 起就存在)。
**版本 X 的指紋 == 現在算出來的 ⇒ X 的成品確實是照這份源碼打的。**
宣告那一步改到的檔案,在戳版當下就已經算進指紋 ⇒ 結構上不可能擋自己;
而「改了 code 卻沒重打包」照樣指紋不同 ⇒ 原本要擋的一個都沒放過。

指紋怎麼算是 daemon 自己的知識,出貨線**不重寫一份**(兩套並存必然漂移):
新增唯讀的 `daemon-version.py --source-state`,`daemon-freshness.mjs` 只負責問與判。
這就是遷移計畫寫的「daemon-freshness 該**搬家**不是加固」。

── 自鎖② 戳版的順序:帳本記的是「還沒戳版」那一刻的樹 ──────────────
`main()` 原本先 `check_or_record()` 再把宣告寫進 changelog。而 changelog 就住在
指紋涵蓋的樹底下 ⇒ 帳本記下來的那棵樹,從寫完宣告那一刻起就不存在了。
實撞(本輪 A/B 重現,見 daemon-version.test.mjs ⑦):
  build-mac.sh 戳版打完 dmg → 同一輪 build-win.sh 走「重打同一版」路徑
  → 現在的指紋(含已戳版的 changelog)≠ 帳本裡那個 → dist/ 已有 dmg
  → 判「版號已對應另一份原始碼」→ **第二個平台永遠打不出來**
而上游 `daemon-sync` 兩個平台都要,缺一不准出貨 ⇒ 出貨線在這裡也走不完。
⇒ 先寫宣告、再記指紋。(這條路是升版,帳本不可能已有該版號 ⇒ 不會中途 die。)
🔴 這個病是 D95 第一輪帶進來的:v0.18.29 打包時 changelog 還在 docs-site/,
  不在指紋範圍內,所以當時不會發作。

── 為什麼要多一本逐檔帳(.version-source-files.json)────────────────
總指紋只有 16 個字,對不上時只講得出「不一樣」。而紅線要求
「回報通過時要說得出它實際比對了什麼」⇒ 逐檔雜湊讓閘講得出**哪幾個檔**變了。
只留最新一版(閘只問最上面那一版),與總指紋帳本一樣排除在指紋之外(自我參照)。

── 閘要留痕(InkStoneCo#48)────────────────────────────────────────
`installer/daemon-freshness-gate-log.md`:擋下、放行、明知故犯放行,三種都記。

── 順手收進來的一筆(同一輪的複驗項)───────────────────────────────
cherry-pick 89dd323:`checkDocsLive()` 的假綠。實測線上 stage 文件站——
那一頁是舊的(135KB 逐版列表、沒有 dcd0132 的 meta-refresh),卻因為內文
剛好含一次 releases 網址而被 `body.includes()` 判過 ⇒ fails=0。修後 fails=1。

實測
  · daemon 0.18.30:Mac dmg 8.3M + Windows exe 22M **同一輪打出來**(修前不可能)
  · 閘判決:status=ok,帳本 ad1106716ae426d2 == 現在 ad1106716ae426d2(涵蓋 228 個檔)
  · installer 測試 257 支全綠;collector 版本產生器 9 支全綠(新增 ⑦⑧⑨)
  · daemon-freshness 自己的演練 15 支:兩個方向各有案例
    (不擋自己/仍擋得住沒重打包/問不出來一律停/留痕)

紅線遵守:只碰 stage;沒推 main、沒併分支、沒碰 prod/GitHub。

Refs: inkstone/arcrun-rag#88

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 15:39:52 +08:00
Leo aeb44c712b fix(88): 撤回「產生端吐裸號」的宣稱——那個 v 是版本線判別器,不能單獨拿掉
總管複驗退回一格(`inkstone/arcrun-rag#88#issuecomment-3082`),退得對。
我照他要的兩條路去查,結論是**選 B,而且原本那個改動本身有 bug**。

## 兩件事要分開講

**① 總管的事實宣稱有一處錯,但他看到的現象是真的**
「這條分支一行都沒動 daemon-version.py」— 不對,上一個 commit 動了 37 行,
含 `version = "v%d.%d.%d"` → `"%d.%d.%d"`。他跑 `daemon-version.py` 得到 `v0.18.29`
也是真的:當時的過渡規則讓「重打已發佈的版本」沿用原本寫法。
⇒ 他跑對了指令、看到真東西,只是把原因歸成「沒改」。

**② 但我的改動確實有 bug,而且比他指出的更嚴重**
`^## v` 這條判斷式有**四份各自為政的拷貝**(daemon-freshness/daemon-in-bundle-gate/
ship.mjs daemon-sync/daemon-version.py),三份只認帶 `v` 的。
產生端一旦戳出 `## 0.18.30(…)`,那三份**不會報錯**,而是往下比對到更舊的
`## v0.18.29(…)` ⇒ **打包出 0.18.30 的執行檔,manifest 卻宣稱是 v0.18.29。**
版本號說謊,靜默,21 站全綠。上一個 commit 就埋著這顆。

## 為什麼不是把三份放寬成 `v?`(我試了,當場被測試擋下)

🔴 **那個 `v` 目前身兼「哪一條版本線」的判別器。**
放寬後 daemon 的閘開始把雲端那條 `## 1.4.47(…)` 當成 daemon 版本撈走
⇒ `daemon-in-bundle-gate.test.mjs` ①⑮⑯ 轉紅,而它們釘的正是 D95 第二輪剛修好的
「出貨線指到錯的 changelog 卻安靜放行」。`daemon-notes.changelogRelFor()` 也用 `/^v/`。

⇒ 「不要 v」要落到產生端,得**先給那些閘一個替代判別器**
  (建議拿 `DAEMON_LINE` 比對,例 `^## v?0\.18\.\d+(`,同時擋「錯的檔」與「錯的線」)。
  那要動 `topReleasedVersion()` 等對外簽章、跨 installer/ 與 collector/,
  **與正在進行的 collector 拆 repo 相衝** ⇒ 不在這條分支做。

## 這個 commit 做了什麼

- 產生端**恢復原行為**(一律吐 `v`):不留「產生端裸號+閘只認 v」那顆靜默炸彈
- 四份拷貝**收成一份**(`daemon-notes.DAEMON_RELEASED_RE`,維持嚴格版)
  ⇒ **零行為變更**,但哪天要改格式只需改一個地方,而不是記得改四個
- 把「為什麼還不能改裸號」釘成一支會說話的測試(`daemon-version.test.mjs` ⑤⑥)
  ——不是註解,是紅燈
- 兩處註解寫清楚接手要先做什麼

**上一個 commit 的另外兩格(release 掛成品、內外對應)未受影響,總管已複驗通過。**

測試:installer 252/252、collector 6/6。
2026-08-18 15:25:22 +08:00
Leo 2e26528bc9 feat(version): 對外號真的不帶 v + release 掛得動成品 + 內外對應由機器產生(arcrun-rag#88)
leo 2026-08-18 當場抓到「剛剛不是說不要 v?」——實查確認不是措辭問題:
規約寫在 wiki,而**產生號碼的那條路照舊帶 v**。這輪修的是產地與載體。

① 對外號從**產生端**就是裸的
   `daemon-version.py` 是 daemon 那條線唯一的版本產生器,一路長到
   產物檔名(Arcrun-<版本>.dmg)、manifest.daemon.version、/api/latest。
   - 新戳的版本一律裸號;`RELEASED_RE` 加 `v?` 讀得懂歷史
   - 🔴 重打同一版**沿用 changelog 上原本的寫法**:selfupdate.go 的
     newerThanCurrent() 是字串不等於就提示更新 ⇒ 把 v0.18.28 改吐成 0.18.28
     會讓每台已安裝的機器收到一個內容相同卻宣稱是新版的**假更新**。
     規約會自己過期:頂上那段變裸號之後,重打路徑吐的也是裸號。
   - 實測:真實 changelog +一段未發佈 → 產生器吐 `0.18.30`(不是 v0.18.30)

② release 掛得動真的成品(本輪的硬前置)
   leo:「assets 都寫 source code,這兩個附檔實際是什麼?是 dmg 還是 go?」
   ——那是 Gitea/GitHub 自動產生的整包 repo 快照。實測既有 release 的
   attachment 數是 **0**,坐實那兩個附檔沒有一個是成品。
   - gitea/github-release.mjs 各補 uploadReleaseAsset/listReleaseAssets
     (兩邊 API 形狀不同是 API 本身的差別,不硬套成同一種)
   - release-lines.mjs 讓**每條版本線各自宣告**自己的成品(assetKeys);
     daemon 線明列 mac/win/msix,**排除釘在 0.15.7 的兩筆歷史遺留**
     ——照「有 file 欄就掛」會把舊檔掛上新版頁面,正是本輪要治的病
   - ship.mjs 建完 release 後掛檔,並**回頭查證**附件真的在頁面上;
     缺任何一個就擋(不留「點下去沒東西」的版本頁)

③ 內外對應由機器產生,不再是總管手寫
   leo:「對應關係記在該次發佈的 release note,不靠號碼本身編碼」
   - 新增 internal-version.mjs:`RAG-20260818-001-<commit7>`
   - 🔴 `formatInternalVersion` 收到空的 artifacts 直接丟例外
     ⇒ **算不出一個沒有成品的內部號**(leo:「內部每次就要出貨 bundle 的版本」)。
     ops-facts 原本記著「這件事目前靠自律,沒有機械閘」——這就是那道閘
   - 序號從**既有 release 的事實**算,不養會漂的帳;tag 與內文都掃
     (08-17 那兩筆內部號寫在 tag_name 上,只掃內文當天會從 1 重編、無聲撞號)

紅線遵守:既有 tag/檔名一個都沒回頭改;沒寫 GitHub(②③ 實測都在 Gitea);
沒部署、沒推 main、沒自己併分支。演練建的 release 與 tag 都已刪除複驗。

測試:installer 252 支全綠(原 233 +新 19);collector 產生器新增 6 支全綠。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 15:13:00 +08:00
Leo 3af210e03a docs: 版本說明頁刪除,改連 GitHub 版本發佈(arcrun-rag#41)
leo 2026-08-17:「這個頁面刪除。」
(同一件事 08-09 就講過:「不要同步,docs 的版本說明直接連回 github 的版本發佈」,
 那時記在該檔檔頭當待辦,理由是「等 releases 累積出足夠版本歷史」——現有 9 筆,理由失效。)

使用者要看版本紀錄 → github.com/youlinhsieh/arcrun-rag/releases。文件站不再自己維護一份。

── 🔴 動手前發現的事:那一頁不只是一頁 ──────────────────────────────
`docs-site/src/content/docs/help/changelog.md` **同時是兩個東西**——文件站的一頁,
以及**雲端引擎 `1.4.x` 的出貨原稿**(`installer/scripts/daemon-notes.mjs` 的 `CHANGELOG_REL`,
被 `ship.mjs` 的 docs-changelog/release-record 與 `github-release.mjs` 讀)。
直接 `rm` 會讓雲端那條線的 GitHub/Gitea 版本發佈點進去變空白。
⇒ 頁面刪掉,**原稿搬去 repo 根的 `CHANGELOG.md`**(旁邊就是根的 `RELEASE_LINE`),
  形狀與桌面版那條線一致:`collector/CHANGELOG.md` + `collector/DAEMON_LINE`。
  `collector/CHANGELOG.md` 一個字都沒動。

── 投影機制整套拆掉(0fb72ae,D95 第四輪,昨天才併進 main)──────────
它存在的唯一理由是把 daemon 段落渲染回這一頁。頁面沒了,它就是沒人用的機制:
  · `docs-site/remark-daemon-changelog.mjs`(整支)
  · `astro.config.mjs` 的 import 與 `markdown.remarkPlugins`
  · `package.json` build 的 `--force`(那是投影的必要條件,不是通用旗標)
  · `collector/.../daemon-notes.mjs` 的 `releasedSections()`/`releasedSectionsFromFile()`/`RELEASED_HEADING`
  · `collector/.../daemon-notes.test.mjs`(4 支全是投影的演練)
`daemon-notes.mjs` 其餘匯出(notesForVersion/checkNotes…)是出貨線在用的,留著。

── 舊網址留一條轉址,不直接 404 ──────────────────────────────────
那個網址掛在側欄上、也印在 landing「這一版改了什麼」旁邊,已隨 landing 部署到使用者
瀏覽器裡。repo 內的連結本輪都改成直接指 GitHub(landing/docs 首頁/側欄),
所以轉址不服務任何內部連結,只接書籤與舊 HTML。它是 `astro.config.mjs` 的一行宣告
(沒有程式、沒有真相源可以漂),而且被 verify-docs 每次出貨夾住 ⇒ 不是要維護的機制。

── ⚠️ 一次真的降級,標在這裡不藏 ────────────────────────────────
`verify-docs.mjs` 原本斷言「線上那一頁有這兩個版號」——沒有頁面就沒有東西可查。
改成斷言「那條轉址還在」(=線上這顆確實是這份原始碼建的)。
**舊斷言抓得到「內容停在上一版」,新斷言抓不到。** 差額由 `release-record` 站承接:
每條版本線都要有一筆版本發佈、內文抽不到就中止——那一站有牙齒且更早跑。
`docs` 站的 ① 那道「這一版寫了嗎」照舊每個目標都問(D65:不准跳)。

實測
  · 建置產物:`1.4.4x`/`v0.18.2x` 命中 0 個檔;`dist/help/changelog/index.html`
    =轉址頁(meta refresh+canonical+a href 全指 releases)
  · 兩種網址(astro preview):`/docs/help/changelog` 與 `…/changelog/` 皆 200,
    內容同一份轉址頁,版本號命中各 0
  · 站內死連結:指向該網址的 href 0 條;pagefind 11 個 fragment 含 changelog 者 0;sitemap 未列
  · 產生鏈完整:`1.4.47`→根 CHANGELOG.md(摘要+4 行內文)/`v0.18.29`→collector/CHANGELOG.md
    (摘要+6 行內文)/`1.4.29`→根(6 行);不存在的版號回 null
  · `node --test installer/scripts/*.test.mjs` 233 支全綠
    (238→233:-4 投影演練、-1 htmlText,兩者的實作都已移除)
  · 建置那句 `/404.htmlEntry docs → 404 was not found.` 是既有行為——
    拿掉 redirects 重建仍出現,與本輪無關

Refs: inkstone/arcrun-rag#41

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 14:35:25 +08:00
Leo 00ccec53f2 fix(docs): 桌面版新版本重新長回文件站——投影不複製(D95 第三輪)
D95 第一輪把 daemon 的 changelog 搬進 collector/ 讓 collector/ 自足,
但文件站的建置沒有任何一步會讀那個新位置 ⇒ 從那一刻起,桌面版的新版本
再也上不了 rag.arcrun.dev/docs/help/changelog/(匿名實測:線上 v0.18.28
有 4 次、v0.18.29 零次;v0.18.29 本來就只到 stage,所以這是未來式的退化,
不是已經在傷害用戶)。

修法:建置時投影,不複製、不改成只給連結。
  collector/CHANGELOG.md
    → collector/cmd/arcrun-app/daemon-notes.mjs 的 releasedSections()(切出已發佈段落)
    → docs-site/remark-daemon-changelog.mjs(remark 階段換掉佔位符)
    → /docs/help/changelog/(網址不變、頁面不變、docs-site 裡零副本)

「哪些段落算已發佈」住在 collector/ 那棵樹——那是 changelog 自己的格式知識,
不是文件站的。方向仍單向:根往內伸手拿,collector 不往外伸手。

順手修掉施工中發現的兩個沉默失敗:
  · Astro 的內容快取只認那一頁自己的檔案有沒有變 ⇒ 只改 collector/CHANGELOG.md
    會建出「成功但內容是舊的」頁面(實測:不加 --force 新段落 0 次命中,加了 4 次)。
    docs-site 的 build 因此帶 --force,理由寫在 package.json 的 //build 與 plugin 檔頭。
  · 佔位符被刪掉時原本會安靜地少掉整條版本歷史 ⇒ 改成當場 throw 並指名修法。

另:第一輪誤把一顆 __pycache__/*.pyc 帶進版控,移出追蹤並補 .gitignore。
(同目錄那些 20MB/11MB 的執行檔與 dmg 是 6b44784/e09f866/c6d7b2c 的舊帳,這輪不動。)

實測
  · 本機建置:v0.18.29 出現 6 次(本輪前為 0);daemon 30 段+雲端 17 段,
    與 D95 之前那一頁的 30/17 完全一致
  · 來源證明:docs-site/src 裡 `^## v0.` 零筆;在 collector/CHANGELOG.md 頂端塞
    v9.9.9 重建 → 頁面命中 4 次,移除後回復
  · 網址:dist/help/changelog/index.html → deploy/docs/…(rsync 鏡射)路徑與本輪前相同;
    線上 /docs/help/changelog 307 → /docs/help/changelog/ 200(平台行為,本輪未動路由)
  · 建置輸出會印「[daemon-changelog] 投影 30 段(v0.18.29 … v0.15.4)|來源 …」
    ——這道閘說得出它實際投影了什麼
  · node --test:installer/scripts/*.test.mjs 與新增的 daemon-notes.test.mjs 共 238 支全綠

Refs: inkstone/InkStoneCo#40

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 14:00:32 +08:00
Leo bd25efaae5 refactor(collector): 剪掉臍帶——collector/ 自己就算得出自己的版本號(D95 第一輪②)
leo 2026-08-17:「身為管理者,你要從頭到尾**不要有很多扭曲**,因為你根本不記得
你做的這些扭曲,**每次都要查**,很直接,源碼、產出物,從 stage 到 prod。」

`collector/` 原本要伸手到 repo 根才算得出自己的版本,共四條:

  ① `daemon-version.py` 讀 `docs-site/src/content/docs/help/changelog.md`
  ② `daemon-version.py` 讀 `ROOT/DAEMON_LINE`
  ③ `daemon-version.py` 的原始碼指紋從 repo 根 `git ls-files collector`
  ④ `changelog-section.sh` 讀 `$REPO_ROOT/docs-site/...` 並 exec
     `$REPO_ROOT/installer/scripts/daemon-notes.mjs`
  ⑤(複驗才發現的第五條)`build-msix.sh` 用 `git rev-parse --show-toplevel`/../../.env
     推 InkStoneCo 頂層拿 MS Store Identity

現在全部落在 `collector/` 內部:

  · `collector/CHANGELOG.md`  ← 新家。**原檔同時裝著兩條版本線**
    (桌面版 `v0.18.x` + 雲端引擎 `1.4.x`),已按版號格式拆開;
    雲端那 17 段原地不動留在 docs-site,桌面版這 30 段搬過來。
  · `collector/DAEMON_LINE`   ← 從 repo 根搬進來
  · 指紋改以 `collector/` 為根算(`cwd=COLLECTOR`、pathspec `.`)
    ⇒ 相對路徑前綴變了、而路徑有進雜湊 ⇒ `FINGERPRINT_ALGO` 3→4,
      照既有設計讓帳本自動整本作廢重記(不要手改 JSON)
  · `daemon-notes.mjs` 實作搬進 `collector/cmd/arcrun-app/`,
    `installer/scripts/daemon-notes.mjs` 變薄殼轉呼叫
    ⇒ **根可以往內伸手,collector 不可以往外伸手**,方向單向
  · `build-msix.sh` 改成往上找「帶著那把鍵的 .env」,
    leo 08-06「腳本自己去讀不要再問人」原樣保留,但不再綁目錄結構

驗收閘:`collector/check-standalone.sh`——不是 grep,是**行為證明**:
把 collector/ 的檔案單獨複製到 repo 之外的臨時目錄、在那裡 git init,
再跑版本計算與打包前置閘。跑得起來=真的自足。

自己發現並修掉的三件:
  · `arcrun-tray/assets/store/` 與 `--setup` 其實還活著(見上一顆 commit)
  · 兩支同名的 `daemon-notes.mjs` CLI 守衛比的是**檔名尾綴**
    ⇒ 兩支一起開火,問雲端版號時 collector 那支先 exit(1),
      薄殼根本沒機會查 docs-site。改成比絕對路徑。
  · `collector/CHANGELOG.md` 的「怎麼出新版」說明若照抄那行標題,
    會被 `daemon-version.py` 的純字串比對當成「有待發佈內容」而誤升版
    ⇒ 說明裡刻意不寫成真的標題,並把這個邊角寫在檔案裡

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 13:05:33 +08:00
Leo cef157c584 chore(collector): 下架死碼 arcrun-tray——但先把還活著的兩塊搬出來(D95 第一輪①)
`collector/cmd/arcrun-tray/`(fyne 版舊桌面殼)自 t193 換 Wails 後退役,
t215 已明文記錄是死碼:三支打包腳本全部只裝 `arcrun-app`。

🔴 **但它不是「整個都死」——複驗發現兩塊還在被現役腳本用:**

  ① `assets/store/*.png`(12 顆 Store 圖示)
     ← `arcrun-app/build-msix.sh:75` 每次打 msix 都 `cp` 它們。
       整個 `git rm` 掉 ⇒ 下一次打 msix 直接斷。
  ② `build-msix.sh --setup`(在 Mac 上建 makemsix 的一次性流程)
     ← `arcrun-app/build-msix.sh:34` 的錯誤訊息就是叫人去跑它。
       刪掉 ⇒ 新機器再也照不出 makemsix,而錯誤訊息會指向不存在的檔案。

⇒ 先把這兩塊逐字搬進 `arcrun-app/`(不重寫),再刪整個目錄。
  死碼下架最容易犯的錯就是「連還活著的那一塊一起丟掉」。

⚠️ **`.git` 不會因此變小**:772 MB 裡的 524 MB 是那三顆二進位
(`ArcrunRAG-mac-unsigned.zip` 16 MB/`arcrun-collector` 15 MB/
`dist-mac/ArcrunRAG-v0.15.7.dmg`)**留在歷史裡**,`git rm` 只停止追蹤。
真正瘦身要重寫歷史(filter-repo)=第三輪,需 leo 親自拍板。

順手更新:`.gitignore` 的三條 tray 路徑(換成 arcrun-app 的 dist-msix)、
`landing/worker.js` 與 `docs/store-submission.md` 裡指向舊素材位置的路徑。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 12:53:38 +08:00
Leo 8764a8870e ship(daemon): 戳版號 v0.18.29——移除要真的收回/不再把套件文件當你的知識
changelog(使用者語言,三條):
· 「移除資料夾」現在真的會收回——先前只是停止監看,已進雲端的知識一筆都不會消失
· 不會再把別人的套件當成你的知識——實測一次 27 張卡有 21 張是第三方 API 文件與授權條款
· 有沒有用 git 不再影響收哪些檔——先前在自己筆記資料夾開一次版控,收檔數就會大幅減少

刻意不寫進 changelog 的:#44 的卡片對照原文檢查。
機制已進 main,但判定結果目前流不到任何人眼前(leo 已裁 A+C,接線尚未做)
⇒ 使用者感覺不到的事不寫進版本說明。

三平台產物:
  dist/Arcrun-v0.18.29.dmg          8.3M
  dist/Arcrun-win-v0.18.29.exe       22M(單一執行檔、自帶同步引擎)
  dist-msix/Arcrun.msix              8.7M(未簽章=送 Store 的正確狀態)

⚠️ 既有缺陷(非本次造成):Homebrew makensis 在這台 macOS 壞掉
   ⇒ Windows 仍只有裸 exe,沒有 NSIS 安裝程式(leo 08-06 要的「正常安裝流程」未兌現)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 23:50:35 +08:00
Leo 895d672177 fix(daemon): 移除資料夾要真的把雲端資料收回(arcrun-rag#46)
leo 2026-08-16 實撞:掛資料夾、同步完成、從清單按「移除」之後——
「我去把 Logseq plugin 刪掉以後,**採集的 wiki 沒消失**」。
內容一筆都沒少,照樣搜得到、照樣 is_embedded=1、AI 照樣拿它回答。

真兇(源碼):app.go 的 RemoveFolder 全文只做三件事——從 WatchFolders 拿掉、
saveCfg、restartWatch,**一次都沒碰撤除**。而撤除機制本身是好的(在部署白名單裡、
有測試、direct.go 真的會觸發它),只是那兩個觸發點都在「還在監看的資料夾裡某個檔
被刪掉」的差異偵測迴圈裡。⇒ 刪一個檔會撤除 /移除整個資料夾不會 。
**在使用者眼裡是同一件事,在程式裡是兩條完全不同的路,只有一條接上了撤除。**

這不只是少一個功能:產品說明卡寫著「確保資料所有權完全屬於使用者而非 SaaS
供應商」,而使用者唯一看得到的收回動作不收回任何東西 ⇒ 知情同意的問題。

修法(沿用既有那條撤除路,不另寫一份):
- drainPendingTakedowns:把既有的「待下架清單逐筆送出」抽成共用函式
- retireRootOnce:資料夾進 retiring_folders 後,把帳本裡真的上傳過的檔排進
  既有的 PendingTakedowns、走同一條撤除路;撤乾淨才刪帳本
- 進度與失敗真因寫進 status.json(level-triggered),App 看到 done 才清設定
- App 是 config.json 的唯一寫入者(兩個行程都寫=互相蓋掉對方的設定)

邊界(本票最危險的地方):path 是相對於被監看資料夾的路徑 ⇒ 兩個資料夾各有
notes.md 時 page_name 與 path 完全相同,撤除一個會連坐另一個。撤除 payload 帶
library(逐根導出,與 ingest 同一個函式算的),workflow 兩個比對節點加「library
相符才殺」。只在兩邊都有 library 時才收緊 ⇒ 舊 daemon 不送/舊卡沒有都退回原行為。

畫面:舊文案「已經上傳的知識卡不會被刪除」技術上是對的,但它替使用者決定了他要的
是「只停止同步」。改成兩個選項各寫一行後果讓他選(預設待 leo 裁)。

測試:go test ./collector/... ./collector/cmd/arcrun-app/... 全綠;
新增 direct_retire_test.go(7)/remove_folder_takedown_test.go(4)/
workflows/tests/takedown-scope.test.mjs(8)。UI 用真 dist + headless Chrome 複驗,
check-cis.sh/check-render.sh 全過。

◐ 未做:真實例端到端(不可逆且 leo 正在該機器上工作,步驟已寫成清單等總管確認)/
workflow 要重新部署才生效/未重打 bundle、未出貨。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 21:35:22 +08:00
Leo 345be49d4d ship(daemon): 戳版號 v0.18.28——把待打包的 28 個 collector 檔真的打包出去
出貨 CP 步驟④(InkStoneCo#44 → comment 2785):changelog 最上面的已發佈段
停在 v0.18.27,而 collector/ 早已往前走 30 個檔(00bface 結構先行/ef5e6c5
規範形 wiki 卡/dce2e15 免金鑰路補齊/c5a9163+e4b7745 ⑦⑩ 關聯段與改名比對
修法/829da02+cc6e500 daemon 不再誤動使用者版控中的檔案)。

changelog「下一版(未發佈)」補上這一輪缺的三條(免金鑰路、關聯段修復、
改名/搬移不誤殺),戳成 v0.18.28(2026-08-16),daemon-version.py 記錄
本版原始碼指紋。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 16:13:52 +08:00
Leo 64cb20a1aa chore(desktop): 戳版號 v0.18.27——把 f71e5a2/578ac2b 兩個 #60 修法真的打包出去
main 上 1e36bb1/f71e5a2/578ac2b 三個 #60 修法都已進了原始碼,但只有第一個
(1e36bb1)進過發佈出去的 v0.18.26 執行檔——後兩個修好之後從沒被打包。
daemon-version.py 唯讀檢查也證實:v0.18.26 這個版號已對應過另一份原始碼
(代表戳完版號之後 code 又動了)。

本次動作:
- changelog 補「下一版(未發佈)」段(用戶語言描述兩個修法),
  daemon-version.py --stamp 戳成 v0.18.27。
- 重打 mac dmg/win exe,三支機械閘(check-cis/check-render/check-tray)全過,
  DMG 掛載實查(兩項/版本號/LSUIElement/簽章)、win exe 內容確認。
- strings 掃兩份執行檔:collector.DetectVaultContext/VaultDirUnder(578ac2b)、
  collector.MarkName/IsMarked(f71e5a2)皆存在——不是只有 commit 在 main,
  是真的編進這次的二進位。
- go test ./... 全綠。

daemon-version.py(唯讀)現在乾淨印出 v0.18.27,不再抱怨版號對應兩份原始碼。
2026-08-13 12:30:08 +08:00
Leo 37a02061dd chore(desktop): 打包器戳上 v0.18.26(2026-08-11)——版號由機器給,這是它自己寫回來的
leo 2026-08-11 20:03 親手跑完三支打包,產物已在:
  dist/Arcrun-v0.18.26.dmg       8.2M
  dist/Arcrun-win-v0.18.26.exe   22M   (在 Mac 上交叉編譯,mingw 已裝)

本筆是打包過程自己改的兩個檔:
- changelog:「## 下一版(未發佈)」→「## v0.18.26(2026-08-11)」
  = daemon-version.py 的設計本意:人寫「未發佈」,機器決定號碼並戳回去
- build/windows/installer/wails_tools.nsh:Wails 打包時同步的版本字串

📌 總管記一筆自己的判斷錯誤:先前把「重打產物」歸類成 leo 的人閘,理由寫「要簽章」。
實際上這三支腳本唯一的簽章是 `codesign --force --deep --sign -`(ad-hoc,不需開發者憑證),
沒有公證、沒有 sudo、沒有互動輸入 ⇒ **總管自己就能跑**。
而且總管讀過那一行才抄指令給 leo,卻沒有回頭推翻分類。
同日第二次「把不存在的關卡架在 leo 身上」(第一次是那份『等 leo 五件事』)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 20:06:11 +08:00
Leo 04b99ad1cf chore(desktop): 重打桌面版 v0.18.26,實測 #59/#60 修復真的在新二進位裡
- main 上 #59(額度卡假訊息)與 #60(vault 辨識+不覆蓋原稿)的修復合併後,
  daemon-version.py 的指紋帳本偵測到 v0.18.25 對應的原始碼已經變了
  (fp a753d866… ≠ 記錄的 33f4bdcb…),代表照舊重戳 v0.18.25 會產出
  「跟 leo 機器上那支同版號、內容卻不同」的二進位——版本號說謊。
  ⇒ changelog 補一段「下一版(未發佈)」,daemon-version.py --stamp
  正確升版成 v0.18.26,v0.18.25 的舊指紋原樣保留不動。

- 用 build-dmg.sh 實際打出 dist/Arcrun-v0.18.26.dmg(8.2M),
  Info.plist CFBundleShortVersionString 確認為 0.18.26。

- 拿同一支打包出來的 arcrun-app 執行檔(--collector direct --once),
  對著本機自建的假 Logseq/Obsidian/一般資料夾三種監看根各跑一輪
  (mock 掉 /portal/daemon/extract 與收卡 workflow,不碰任何真實帳號/額度):
  vault 兩種都落在 .arcrun-rag/wiki/cards/(隱藏,筆記軟體不掃描)、
  一般資料夾落在 system-dev/wiki/cards/(行為不變);
  三種都故意預先放一張同名舊卡,跑完舊卡被備份成 <name>.md.bak-<nanos>、
  新卡才落地,沒有無聲覆蓋;Logseq 的 journals/*.md 原稿位元不動。

不動 collector/cmd/arcrun-app/arcrun-app(那支是舊的、脫離版控管理的建置產物,
本次 go build ./... 順手覆蓋過又還原,不屬於這次的變更範圍)。
2026-08-11 14:05:57 +08:00
Leo 3862a1a877 chore(daemon): 版號戳到 v0.18.25,補迎新精靈的 changelog 段(Gitea #23)
daemon-version.py 的指紋鎖已擋下 v0.18.24(onboarding wizard 合併後
原始碼與該版指紋不符)。補上「## 下一版(未發佈)」段描述兩步引導
精靈,重跑 --stamp 機械戳出 v0.18.25,三平台(dmg/exe/msix)已重打
包並逐一拆開驗證內嵌版本,見 #23 留言。本次僅打包,未出貨。
2026-08-09 17:41:37 +08:00
Leo e7a72d74e1 daemon: 首次啟動改成兩步引導精靈(Gitea #23)
舊版 onboarding() 只有兩行字+兩顆按鈕,沒解釋 Arcrun 是什麼,也沒有
「我做到哪了」的感覺;『我還沒有知識庫』點下去就丟去外部網址,
回來以後要自己想起來要按哪顆鈕才能連線。

改成兩步小精靈,每步都有步驟點(1/2):
① 認識 Arcrun:三句大白話講清楚在做什麼,不用任何內部詞
② 連上或申請知識庫:『已經有了』/『還沒有』並排兩張卡,
   『還沒有』那張當場講清楚『回來這裡按哪顆鈕』,不是丟出去就沒事

連線邏輯(Connect/showConnect)完全沒動,接的是既有後端;
連線成功後落回首頁,首頁本來就有狀態時間軸+自動種好的範例資料夾
(default_library.go 的 P4),使用者不必再多做一步就看得到
『丟檔案 → 知識卡』整條路跑起來。

只動前端(main.js+style.css),零 Go 改動。check-cis.sh 全過;
用 mock window.go.main.App 在瀏覽器截圖驗過兩步畫面(截圖見 issue #23 留言)。
2026-08-09 15:03:23 +08:00
Leo ef8126201c P8 短板齊平:模型品質實測(granite 否決、qwen3-30b 定案)+額度撞牆三句話接上畫面
① docs/benchmarks/p8-extractor-quality/:leo 真筆記 8 篇 × 5 模型可複驗實測
   (production 同 prompt/參數、CF 原生 usage.neurons 計量)——
   scout 84 n/檔(119 檔/日)→ qwen3-30b 43 n/檔(232 檔/日)品質不降反升;
   granite 最便宜(858 檔/日)但 5/8 缺段、三元組全滅=否決。
   誠實結論:免金鑰路物理撐不起「幾千篇當天匯入」(差 13 倍),
   正解=消化節奏(3,000 篇約 13 天背景跑完、新筆記優先)+出口(Gemini/付費/ollama)。
② 撞牆體驗:quota_message 08-07 就在 status.json,App 從來沒接——
   app.go pickQuotaNotice(過期快照不說謊)+ main.js cardQuota(三句話卡)+
   progress.go ClassifyFailure 補認三句話(原本掉「其他」)。
   Go 兩 module 全綠;瀏覽器實載 dist 深淺兩主題截圖、console 0 錯。
③ 公開鏡像排除 docs/benchmarks(輸入是 leo 私人筆記)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 00:44:34 +08:00
Leo 779a1b801e feat(t215): 每個知識庫顯示是否要更新,落後就給 install 頁連結
leo 08-08:「在每個知識庫會看到其他需要更新的,在每個知識庫上顯示是否要更新,
如果要,加開啓 install 頁的連結」。小幫手以前只提示自己(daemon 本體)要不要
更新,使用者連著多個雲端知識庫時完全看不出哪一個落後。

- collector/cloud_latest.go:EvalCloudUpdate + FetchLatestCloudRelease(30 分鐘節流),
  判準與 portal 版本卡 loadVersion() 同一套(bundle_version vs install.arcrun.dev/
  api/latest 的 release,semver 逐段整數比較),不是 t103 minCloudRelease 那把相容
  底線,避免同一個知識庫在兩處得到相反答案。
- direct.go/sync_status.go:每輪同步順帶算好每個帳號的 CloudUpdateKnown/
  CloudUpdateStale/CloudLatest,寫進 status.json。
- app.go:GetState 把這些欄位接進 UIAccount,供前端讀。
- main.js/style.css:首頁新增「知識庫版本」卡(每庫一行,落後才出現「前往安裝頁
  更新」按鈕,帶 email 預填);側邊欄庫名旁加警示點;各庫頁也顯示同一行版本狀態。
  查不到版本(連不上/latest 暫時查不到)一律誠實說「查不到」,不當成「已最新」。

已用假 window.go 在瀏覽器實測四種情境(落後/已最新/連不上/查得到 mine 但查不到
latest)+零知識庫的 onboarding 頁+深色模式,畫面與按鈕行為皆符合預期。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 00:21:40 +08:00
Leo 2bbfa453d3 更新畫面那一行由 changelog 機械導出,不再出貨當下手工排版
leo 2026-08-08 真機看到 v0.18.24 的更新畫面:
「不要這麼長的散文,簡短講改了什麼,細節去 docs 讀。」
畫面是一整面文字牆,`**粗體**` 還原樣露出來。

真兇不是文案,是**最後一哩沒有機制**:
- 單一真相源(changelog.md)08-06 就立好了(30f726b),
  changelog-section.sh 也有 --check 閘擋「忘了寫更新內容」。
- 但它的**投影**那一段是 `tr '\n' ' '`——把整段散文壓成一坨,
  而且 grep 全 repo **沒有任何東西呼叫它**(build-*.sh 只用 --check)。
- ⇒ 每次出貨都是有人臨時寫一段 python 折行塞進 manifest,
  每次重寫一次、沒人檢查結果長什麼樣。

為什麼只能是一行短句(不是排版沒做好):
  main.js:215 是 `<div class="d">${esc(u.notes)}</div>`
  ① esc() ⇒ 任何 markdown 符號都會原樣露出
  ② .d 沒有 white-space:pre ⇒ 塞 \n 也不會分行

改法:
- 新增 installer/scripts/daemon-notes.mjs =**唯一的投影器**:
  只取 changelog 每條的粗體標題、串成一行、超長截斷並導去說明文件。
- changelog-section.sh 的投影段改成委派給它(--check 閘原樣保留),
  兩邊同一份規則不會漂移。
- ship.mjs 新增 notes 步驟:每次出貨自動套用,手改過的會被改回來。
- release.mjs 的 verifyManifest 加第四條規則:notes 必須存在、無 markdown、
  單行、不超長,且**等於 changelog 的機械投影**——手改一律擋下。

實測:
· v0.18.24/23/22 三版各自產出 62/51/80 字,零手工調整
· 手改成一段散文 → verify-manifest exit 1,兩條規則都抓到
· 再跑一次管線 → 自己改回正確那一行
· 用 daemon 真實 style.css + main.js 的真實 markup 渲染兩版對照並截圖:
  舊 576 字(含 8 個原樣露出的 **)vs 新 62 字三行讀完

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 21:47:31 +08:00
Leo 5ea6b755d9 修 stage 上 daemon 下載連結誤指 prod GitHub 的 bug,v0.18.24 上架 staging 驗收
daemonOf()(installer/oauth-prototype/worker.js)原本無條件把下載連結組成
raw.githubusercontent.com/youlinhsieh/arcrun-rag-bundles/<sha>/...——這對 prod
(bundleBase 是 jsDelivr @sha,需要換宿主避開單檔 20MB 上限)是對的,但對
staging(bundleBase 本身就是可直接讀檔的 Gitea raw root)會把 staging 的
commit sha 套進 prod 的 GitHub repo 路徑,下載必然 404/502。改成只有偵測到
prod 的 jsDelivr `@sha` 格式才換宿主,其餘直接用 base 本身當下載來源。
cacheKey 加版本後綴避免邊緣快取繼續吐修復前的舊 JSON。

wrangler.toml [env.staging] BUNDLE_BASE 釘子換到推了 v0.18.24 daemon 的
staging bundle commit(31a6ccf)。

diagnostics_stage_manual_test.go 的模擬出貨版本號更新到 v0.18.24(跟目前
實際出貨版本一致)。

實測(stage,真下載+真掛載):
- /api/latest:daemon.version=v0.18.24,下載連結指回 staging 自己的 Gitea repo
- /download/mac:200,sha256 與本機建置產物一致,掛載後
  CFBundleShortVersionString=0.18.24,codesign 有效
- /download/win:200,sha256 一致,exe 內嵌版本字串含 v0.18.24
- TestDiagnosticsStageManual(真 stage youlin 帳號)PASS,匯出診斷檔 JSON
  全文無本機絕對路徑

紅線核對:prod 全程未碰(.github-armed 不存在、arcrun-rag-bundles prod repo
未 clone/push);只動了 staging bundle repo(Gitea arcrun-rag-bundles-staging)
與 arcrun-rag-installer-staging worker。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 17:24:31 +08:00
Leo 0cdbda8694 v0.18.24 changelog 補齊(診斷檔 engine 狀態+路徑遮蔽)+三平台打包驗證
changelog 原本只涵蓋 5fcc139/5effcb2/9f1ca58 三項,補上今天另兩項使用者可見改動:
- 疑難排解按鈕的診斷檔現在也看得出小幫手還在不在跑(8eeb393)
- 診斷檔不再洩漏本機絕對路徑(4f81ad6)

三平台已本機打包驗證(未出貨、未碰 stage/prod):
- Mac dmg:CFBundleShortVersionString=0.18.24、codesign 有效、內含 Applications 捷徑
- Windows exe:單一自帶同步引擎執行檔(NSIS 安裝程式因本機 makensis 工具鏈壞掉暫缺,非本輪修復範圍)
- MSIX:AppxManifest Identity Version=0.18.24.0

.version-source.json 的 v0.18.24 指紋隨之更新(fingerprint 現涵蓋 8eeb393/4f81ad6 的 collector/ 改動)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 16:59:18 +08:00
Leo 0c6fb43117 fix(t213): 診斷檔遮蔽引擎錯誤訊息裡的本機絕對路徑
engine.detail/engine.last_error 在同步引擎沒在跑時,會把 collector 開機
橫幅的完整原文帶出來(direct.go RunDirect 的「監看 %s → %s」那行),其中
含監看資料夾的絕對路徑(如 /Users/xxx/Desktop/...)。這是上一輪(第二/
三輪)已發現但標記「附帶發現、未修」的洞——首頁在同一狀態下本來就會顯示
同一句話(不動,留給另案判斷),但診斷檔現在會被匯出成檔案交給外部人看,
風險層級跟留在托盤畫面上不同,這輪把它遮掉。

新增 redactLocalPaths(),只在 buildDiagnosticsPayload() 組裝 engine 欄位時
套用:把訊息裡看起來像本機絕對路徑的片段換成「…/<路徑最後一截>」,
http(s) URL 先放行原文(避免 URL 裡的 / 被誤判成本機路徑)。只動
diagnostics_export.go 這一端,不改 describeStatus()/collectorFailure()/
direct.go 的訊息本身——那些同時是首頁托盤畫面在用的同一組憑據。

擴充 diagnostics_engine_e2e_test.go(真執行檔+真 supervisor 子行程,零
網路零帳號):監看資料夾改用真實絕對路徑,證明子行程停擺後整份匯出 JSON
找不到該路徑,同時 last_error/detail 仍讀得出「引擎沒在跑、以及為什麼」
(含監看資料夾的名字)。另加 8 個 redactLocalPaths 純函式單測。

go build/go vet/go test ./...(collector+arcrun-app+supervisor 三個
package)全綠;diagnostics_stage_manual_test.go 維持預設 SKIP,本輪未碰
網路/stage。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 16:36:20 +08:00
Leo 4cf09bf8ca test(t213): 補一支可獨立重跑的 stage 手動驗收,回應總管複驗要求
總管指出「本機測綠 ≠ stage 上按得出來」,且上一輪驗完就刪了測試檔,
讓總管沒有東西可以自己重跑複驗(不像 diagnostics_engine_e2e_test.go
那支,總管自己重跑就拿到 PASS)。

新增 diagnostics_stage_manual_test.go:留在版控裡(RUN_STAGE_DIAGNOSTICS=1
+ STAGE_ACCOUNT_JSON 才會跑,預設不影響一般 go test ./...),總管/leo
可以用自己的憑證隨時獨立重跑,不必等我。這次順帶修正版本號問題:
buildDiagnosticsPayload() 是被測試行程自己呼叫的(同一個 package main
的 version 套件變數),先前直接 go build 只讓子行程帶了新版號,
daemon_version 欄位仍印出編譯測試二進位檔的預設值 "dev"——這次改成
呼叫前直接賦值 version(比照 build-mac.sh 的 ldflags 精神,讓輸出更接近
真實出貨版本),確認過改動不影響其他測試(version 用完 t.Cleanup 還原)。

真跑一次(stage youlin 帳號,真雲端):daemon_version="v0.18.23"、
accounts[].cloud.bundle_version="1.4.22"(確認不是 null)、同一組
progress(total:2 pending:1 unreadable:1)在「活著」與「sup.Stop() 停了」
兩次匯出裡 engine.alive 正確反映 true/false。JSON 全文(含檔案路徑證據)
已回報總管。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 14:51:16 +08:00
Leo 8baa9170da feat(t213): 診斷檔補 engine 區塊,補上考卷 Q2「還在不在跑」缺口
pending=0 以前只能證明「現在沒有排隊的」,證明不了「真的做完了」還是
「daemon 早就掛了沒人知道」——因為診斷檔完全沒有時間資訊。

local.engine 不發明第二套判斷法:直接借用首頁狀態列本來就在用的
collectorAlive()/collectorSyncing()/collectorFailure()/describeStatus(),
只是把這些憑據也寫進 JSON(alive/syncing/crash_looping/headline/detail/
last_sync/last_activity_at/seconds_since_last_sync)。last_error 只在
alive=false 或 crash_looping=true 時才附上,避免健康行程的開機橫幅
被誤讀成錯誤訊息。

新增 diagnostics_engine_e2e_test.go:真的建執行檔、真的用 supervisor
拉起子行程、真的 Stop() 停掉它,驗證同一組 progress 數字在兩種情境下
engine.alive 正確反映活/死(永久迴歸測試,零帳號零網路,快且穩定)。

stage(youlin 帳號)實測驗證見 system-dev/wiki/status.md:兩次真實匯出
(活著/停了)JSON 全文、四題考卷重跑 4/4。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 14:37:53 +08:00
Leo 92fecb48c1 stage 驗收:t213 兩半+t210+t209 全鏈打通(InkStoneCo 總管交辦)
- changelog 補 v0.18.24(下一版)條目:失敗真因不再被抹掉/首頁改統計/
  疑難排解按鈕搬進小幫手本體——三批合一次驗收
- .version-source.json 記錄 v0.18.24 指紋(daemon-version.py 版本閘產物)
- staging 安裝器釘子換到 arcrun-rag-bundles-staging@51f84bf(source Arcrun@8447165,
  含 93b1140 新端點 GET /portal/daemon/diagnostics + 8447165 portal 按鈕文案)

驗收方式:deploy-all.mjs 全量部署到 youlin 測試實例(stage)、collector direct --once
真跑一輪(3 ingested/2 unreadable,格式不支援)、buildDiagnosticsPayload() 真跑產出
合併診斷 JSON(本機 Progress/FailureBreakdown + 雲端 /portal/daemon/diagnostics)。
四題考卷用該 JSON 逐題核對,詳見本次回報。

未動:prod bundle repo/github-arm/publish-github(紅線,一步未碰)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 13:53:53 +08:00
Leo e5d8cee58b feat(t213): 匯出診斷檔搬進 arcrun-app 本機端(phase 2,接手 t210 落地後的 Progress/FailureBreakdown)
t210(commit 5effcb2)已把「總量進度」「失敗分類」算好並寫進 status.json(現況快照,
不隨閒置歸零)——本次直接讀那兩個欄位,不另算一套(首頁與診斷檔同一組數字)。

新增:
- collector/cmd/arcrun-app/diagnostics_export.go
  - App.ExportDiagnostics():合併本機(daemon 版本/自我更新狀態/Progress/
    FailureBreakdown/略過檔案樣本)與雲端(每帳號打新端點 GET /portal/daemon/diagnostics,
    X-Arcrun-API-Key 認證)成一份 JSON,彈系統存檔對話框存下來
  - mergeDiagnostics 抽成無 IO 純函式,方便測試
- collector/cmd/arcrun-app/diagnostics_export_test.go:5 個離線測試,涵蓋
  同首頁數字/分類名稱原樣照抄不自己判斷/失敗檔名 basename-only/略過清單省略欄位/
  帳號層 cloud 與 cloud_error 互斥
- frontend/src/main.js:「版本與更新」頁加「疑難排解」卡片+匯出按鈕

守住的規則:
- Progress/FailureBreakdown 原樣接住 status.json,不重掃 manifest
- 分類名稱字串只認 collector/progress.go 的 ClassifyFailure,本檔不判斷任何分類
- 失敗檔名 basename-only:借用既有 buildSkipped() 輸出(本來就是 basename+白話標籤)

驗證(實跑,非設計稿):go build/vet/gofmt 全乾淨;go test ./...(collector+
arcrun-app)全過;check-cis.sh/check-render.sh 視覺機械閘全過;wails build 成功
產生含 ExportDiagnostics 的 bindings;拿本機真實 config.json(2 個真帳號)+真網路
跑 buildDiagnosticsPayload():真拿到 update_check.latest=v0.18.23、真列出本機
略過檔案(basename)、兩個真帳號誠實回 cloud_error(線上新端點還沒部署,與總管
實測結果一致)。未在真實 GUI 點過按鈕(本機正跑 production Arcrun.app,避免干擾)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 13:09:48 +08:00
Leo b3f63f40fb feat(t210): 首頁改統計,不再逐檔解釋——Evan「9000/101/20 兜不起來」的病
地基(progress.go 的 SyncProgress/ClassifyFailure/BuildFailureBreakdown,
eeb35ba)已經算好「總量」與「失敗分類」,但沒人接到畫面上:首頁仍在用
本輪計數(extractedOK)+逐檔白話翻譯(humanizeFailure),導致 leo 08-08
轉述的病——三個數字互相對不起來,使用者無法判斷「還在跑」還是「壞了」。

接線(collector/direct.go):
- runDirectOnceRoot 現在也回傳這一根資料夾的 SyncProgress/StuckReasons
  (rootProgress),RunDirectOnce 跨帳號跨資料夾 Add() 累加成總量。
- G-6.2 的 SkippedDocCount(讀不了的檔,根本沒進 manifest)併進
  Unreadable/Total——這是 Progress() 算不到的部分,由呼叫端補齊,
  維持不變式 Total == Done+Pending+Stuck+Unreadable。
- 「送不上去」的分類統計=Stuck 的 LastError 原文+Unreadable 重用
  convert.go 既有的 ErrUnsupported,一起餵給 BuildFailureBreakdown。
  分類判斷全程只經過 progress.go 的 ClassifyFailure 一個接縫,
  direct.go/app.go/前端都不認得任何分類名稱字串(留給 t214 之後
  改資料驅動時只動一個檔)。
- SyncStatus 新增 Progress/FailureBreakdown 兩個欄位,兩者都是每輪從
  manifest/掃描結果原地重算的現況快照,不進 CarryForwardActivity——
  斷網或閒置一輪不會被清成 0。

畫面(cmd/arcrun-app/app.go+frontend/src/main.js):
- 移除 humanizeFailure/buildFailures/UIFailures 那套逐檔白話翻譯,
  改用 UIProgress(Total/Done/Pending/CantSync/Groups);前端 cardProgress
  只把後端給的 category/count 陣列原樣印出,不分支、不排序、不認分類名。
- 「送不上去」預設摺疊(<details>),展開只有分類與份數,不逐檔列名、
  不解釋、不給解法;細節導向「開啟使用說明」。
- 保留 buildSkipped 的「讀不了的檔」卡片(那是另一件事),但份數已併入
  Unreadable。
- 移除「總計」卡片裡用本輪計數 extractedOK 的「份已整理」——上方狀態
  時間軸的「上次 N 份」與下方矛盾(1 份 vs 0 份已整理)的病因直接消掉,
  改用 cardProgress 的累計「已送上去」。

驗證:
- go build ./... 與 go test ./...(collector/cmd/arcrun-app/
  cmd/arcrun-tray 三個 module)全綠。
- 新增 collector/progress_wiring_test.go:真跑 RunDirectOnce 湊出
  Done/Pending/Stuck/Unreadable 四種狀態同時存在,驗四數字相加等於
  總數(leo 驗法①);再跑一輪「什麼都沒發生」驗數字不歸零(驗法③)。
- check-render.sh 視覺機械閘綠(lockup 底板/深色模式)。

殘項(誠實標記,未完成):
- 真機驗收(leo 08-08 驗法④:拿 Evan 的情境走一遍)未做,需要 leo 或
  封測者在實機驗證。
- 「開啟使用說明」目前連到既有 docs 首頁,尚無 t210 分類對應的 FAQ 頁
  (tasks.md 已記為相依項)。
- t213 診斷檔尚未消費這組新欄位(tasks.md 記載該任務等本任務讓路)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 12:45:12 +08:00
Leo 2b06a0456d v0.18.23 changelog(用戶語言)+三平台打包
指紋閘擋下重打同版號(「兩個內容不同卻都叫 v0.18.22=版本號說謊」),
照它要求先寫 changelog 才放行——這個閘今天第一次真的攔到人,有效。

打包踩到一個坑:dmg 壓製回 resource temporarily unavailable,
真因是前一次對照實驗留下的 /Volumes/Arcrun 1 掛載點沒卸載。
hdiutil detach -force 後即通過。記下來免得下次誤判成打包壞了。
2026-08-08 03:10:11 +08:00
Leo edc9916b2f 修好:Mac 自我更新斷七代(dmg 餵給解 zip 的 ditto)+殘留 staged 旗標騙人重啟
症狀一:manifest.daemon.mac.file 出貨格式從 zip 換成 dmg(build-dmg.sh),
但 applyStagedAndRestart 只認 zip(ditto -x -k)⇒ ditto: Couldn't read PKZip
signature,Mac「檢查更新」從 v0.18.5 斷到 v0.18.22(leo 真機截圖實測)。
症狀二:手動裝好新版後仍顯示「已下載完成,重新啟動就會套用」——staged.json
判準是「有沒有下載過」而非「目前版本 vs 最新版本」。

- extractAppFrom 依副檔名分流 .dmg(hdiutil attach -mountpoint)/.zip;
  Windows 補上真正的自我更新(rename 正在跑的 exe → 搬新版 → 原路徑重啟,
  併檔後 Windows 只有單一 exe,「開資料夾」是併檔前的殘留假設)。
- CheckUpdate 加 stagedDecision 純函式:已追上或 staged 版本過期就清掉
  殘留(連磁碟檔案一起清,不只清記憶體)。
- 新增 selfupdate_test.go:用真的 hdiutil 造 dmg 驗證解壓(不是 mock)。

Mac 端到端已用真的線上 manifest/真 v0.18.22 dmg 驗證整條鏈路(含用舊寫法
對同一份真檔案重現原始錯誤,證明修法對症)。Windows 只驗到 GOOS=windows
交叉編譯成功,真機行為待 leo 用 VMware Fusion 驗證——詳見 status.md。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 02:58:01 +08:00
Leo 837e5e8052 出 v0.18.22:aa65efa 積壓分批四件套+新增預設庫,全平台已打包驗證
changelog 補上用戶語言的更新內容(daemon-version.py --stamp 依此機械
算出版號、記錄原始碼指紋)。三平台已打包並拆開驗證內嵌版本 == v0.18.22:
- Mac DMG:Info.plist CFBundleShortVersionString=0.18.22,
  arcrun-app --collector --version → v0.18.22,codesign 有效
- Windows exe:ldflags 內嵌 v0.18.22(無舊版號殘留),
  build-win.sh 內建探針確認 --collector --version → v0.18.22
- MSIX:AppxManifest Identity Version=0.18.22.0(真身分,非佔位值),
  內嵌 Arcrun.exe 同樣是 v0.18.22

尚未出貨(ship.mjs/purge jsDelivr/D20 開閘),產物留在
collector/cmd/arcrun-app/dist(-msix)/(gitignored,不進版控)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 00:27:44 +08:00
Leo 361f1f1f9d 新增全新安裝的「可刪除預設庫」(leo 2026-08-08 拍板)
全新使用者第一次連上知識庫時,自動在 Documents 建一個
「Arcrun 範例庫(可刪除)」,裡面放三份 .md 示範內容,
不必自己選資料夾就能立刻走完「丟檔→知識卡→搜得到」。

四條紅線都有測試釘住(default_library_test.go/
default_library_e2e_test.go,含真的建 binary 跑 --dry-run 驗證):
1. 可刪——刪掉就真的消失,marker 檔擋住重種
2. 只有第一次——已有帳號的機器不會冒出來
3. 不污染——只在自己的資料夾裡寫檔
4. 不留幽靈資料——資料夾被刪後,config 殘留引用會被清掉(GetState 自我修復)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 00:22:03 +08:00
Leo 8ad6f25ba3 出貨 v0.18.21:釘子 → c1566cf(Windows 修復鏈+首發 MSIX 已送達封測者) 2026-08-06 23:18:00 +08:00
Leo c952d66e41 MSIX 帶入真 Store Identity(從頂層 .env 自動讀)+credentials-map 登錄:別再問 leo 要這三個值 2026-08-06 23:04:53 +08:00
Leo 5c0bddacc4 v0.18.20:失敗清單改摺疊式+修指紋閘的自我參照
## UI(leo 實測版面被壓扁)
失敗清單改用原生 `<details>`:只列檔名、旁邊三角形、**預設收合**,
點開才看原因。先前把原因攤在檔名旁邊,中文檔名被擠成一行一個字。

## 🔴 指紋閘三修:帳本自己不能算進指紋(經典自我參照)
`.version-source.json` 就住在 `collector/` 底下 ⇒ 戳版號寫入它、指紋跟著變
⇒ **同一輪連續打 win/mac/msix,第二支就被自己的閘擋下**(實撞)。
查證過才修(沒有亂放寬):
  · `gen-icons.py` 產的 .ico **位元一致**,不是它
  · `dist/` 早就 gitignore,不在名單裡
  · 真兇只有帳本
⇒ **只排除帳本一個檔**。一度想順手排掉整個 `build/`,那會讓「換 app icon」
  不算原始碼變更=在閘上挖洞,已收回。
驗:連跑兩次版本號相同(冪等)。

## 三種產物都備齊(在共用資料夾)
`Arcrun-win-v0.18.20.exe`/`送審用/Arcrun-v0.18.20.dmg`/`送審用/Arcrun-v0.18.20.msix`
⚠️ MSIX 的 **Identity 三值仍是佔位**(`PLACEHOLDER.ArcrunRAG`)⇒ **不能送 Store**,
需 leo 從 Partner Center 提供 `IDENTITY_NAME`/`PUBLISHER`/`PUBLISHER_DISPLAY` 再重打。

## 未送達
線上仍 v0.18.8。推遠端需 leo 開 D20 閘。
2026-08-06 22:39:42 +08:00