Commit Graph

182 Commits

Author SHA1 Message Date
Leo 6e80296362 sync: collector/ 同步自 inkstone/arcrun-rag@d761106(桌面小幫手 0.18.41) 0.18.41 2026-08-27 19:55:35 +08:00
Leo 58d47d9080 sync: collector/ 同步自 inkstone/arcrun-rag@85cb90d(桌面小幫手 0.18.40) 0.18.40 2026-08-27 14:03:50 +08:00
Leo e3f1d0177d sync: collector/ 同步自 inkstone/arcrun-rag@f2a1121(桌面小幫手 0.18.39) 0.18.39 2026-08-27 12:54:08 +08:00
Leo 4d966b7922 sync: collector/ 同步自 inkstone/arcrun-rag@c6cf717(桌面小幫手 0.18.38) 0.18.38 2026-08-26 21:04:03 +08:00
Leo a1786135d5 sync: collector/ 同步自 inkstone/arcrun-rag@1edbbb2(桌面小幫手 0.18.37) 0.18.37 2026-08-26 17:22:08 +08:00
Leo d028a66625 sync: collector/ 同步自 inkstone/arcrun-rag@45b74a4(桌面小幫手 0.18.36) 0.18.36 2026-08-24 18:26:55 +08:00
Leo f1658fd2ad sync: collector/ 同步自 inkstone/arcrun-rag@a39cea2(桌面小幫手 0.18.34) 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>
0.18.30
2026-08-18 21:33:34 +08:00
Leo a35421e2e2 sync: collector/ 同步自 inkstone/arcrun-rag@99cc64f(桌面小幫手 0.18.33) 0.18.33 2026-08-18 21:23:45 +08:00
claude-code 0d3b853249 docs: 說清楚這個 repo 是什麼、成品在哪、跟 arcrun-rag 什麼關係(D95)
leo D95 的要求是「不要有很多扭曲,因為你根本不記得你做的這些扭曲,每次都要查」
⇒ 打開這個 repo 第一眼就要答得出三題:這是什麼產品/去哪拿/跟隔壁那個什麼關係。

過渡狀態(collector/ 還留在 arcrun-rag)也寫在裡面——
不寫的話,下一個人查到兩份源碼會以為自己找錯地方。

票:inkstone/InkStoneCo#40
2026-08-18 18:45:26 +08:00
claude-code ac01dd5e7b chore: 編譯產物不再進版控——抽成獨立 repo 的同時把 626MB 死產物留在原地(D95)
抽取方式:git subtree split --prefix=collector(保住 171 顆 commit 的歷史)
        → git filter-repo 剔除 8 條純編譯產物路徑(.app/dmg/下載回來的安裝檔)

剔掉的只有編譯輸出,**原始碼一顆 commit 都沒少**:
174 → 171,少的三顆是「產物同步 v0.16.0」這類只動二進位的 commit。
228 個檔案與 arcrun-rag 的 collector/ 逐檔 sha 比對:0 個不一致。

票:inkstone/InkStoneCo#40(D95 結構拆分)
2026-08-18 18:44:48 +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 bd9478c9a8 Merge remote-tracking branch 'gitea/feat/machine-source-label' 2026-08-18 16:59:20 +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
claude-code 395cad8f12 feat(collector): 每一則知識都答得出「原稿在哪一台機器上」(inkstone/mira#6)
現況只缺這一格:雲端的麵包屑已經有資料夾多層路徑、檔名、片段錨點
(kb://RFP/design.md#0),唯獨答不出「哪一台機器」。

leo 2026-08-18 拍板的三層,由強到弱:
① 抓得到就用真實可讀的名字 → `<使用者>@<主機名>`(實跑:youlinhsieh@Leo-MBA)
② 使用者要能改成自己看得懂的稱呼 → config.json 的 machine_label(實跑:教育部 Leo 的 Mac)
③ 什麼都抓不到也要保證兩台不同 → unknown@<隨機 8 hex>,持久化在 machine.json

做法照既有的 library 走,不新增第二種:
- daemon 逐筆隨 payload 送 machine/machine_label(收卡、總覽卡、下架、舊直送四處)
- 雲端寫進 block 的 metadata_json,與 source/source_path/library 並排
- 三元組寫進 machine slot(slot 由 matrix/arcrun 的 seed 補,見該 repo 分支)
- upsert/下架的比對規則照抄 arcrun-rag#46 對 library 的處置:**兩邊都有值才收緊**
  ⇒ 既有資料(沒有這一格的)行為一字不變,不會變兩份、也不會撤不掉

🔴 machine **不混進 source_uri**:portal 的 srcLocalPath() 直接把 kb:// 之後整段當本機
   相對路徑用,混進去會吐出不存在的路徑,而且舊資料的第一段會被誤讀成機器名
   ——那就是「憑空長出假機器」。分成獨立欄位,舊資料就誠實地空著。

ID 一鑄就不再重算(machine.json):user@host 會變,重算會讓同一台機器的舊知識
突然掛到「新機器」底下。改名走 machine_label,不碰 ID。

驗過(youlin stage,實測輸出見 inkstone/mira#6 回報):
- 真 daemon 跑 direct --once → 每個 block 的 metadata 都有
  machine=youlinhsieh@Leo-MBA、machine_label=教育部 Leo 的 Mac
- 同一相對路徑、兩台機器 → block 各留一份、三元組 4 筆並存,沒有互相刪掉
- 既有資料(無 machine)照樣被同一台機器 upsert 掉,沒有變兩份
- workflows/tests/machine-scope.test.mjs 13 passed;takedown-scope 8 passed;isdoc 21 passed
- collector go test ./... 全綠
2026-08-18 16:50:23 +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 577d883e94 fix(collector): 收檔策略改看資料夾裝了什麼,不看有沒有版控(arcrun-rag#104 第二層)
leo 2026-08-16:「你不需要判斷有沒有 git,我的 KB 筆記庫也有 git,
是否用 github/gitea 追蹤完全沒意義。」

第一層(80aed82)已把「哪些目錄算雜訊」改成不看版控;本輪處理
「這個資料夾整體該用哪種收法」(all/curated-wiki/docs-only)那一層。

定時炸彈(實測,非推測):`~/Documents/KB` 是 leo 的真知識庫,今天沒有
`.git` ⇒ all ⇒ 全收;而它**真的有 system-dev/wiki**(他在筆記庫裡也裝過
template)⇒ 只要有人跑一次 `git init`,就翻成 curated-wiki,靜默塌成十幾張。

新判準(collector/foldershape.go):三條同時成立才算軟體專案——
① 找得到專案檔 ② 原始碼 ≥ 20 ③ 原始碼 × 4 ≥ 文件數。三條都不看版控。
第③條擋的是「筆記庫裡放了一整份下載回來的範例專案」。

實測分得很開:KB=0/15/5915(筆記庫)|InkStoneCo=213/3355/6115(專案)
|pms=21/37/22(專案)。InkStoneCo 與 pms 的模式都不變。

判準換成內容,證據也換成內容:`IngestPlan.RepoRoot`(.git 在哪)改成
`IngestPlan.Shape`(實測形狀),Reason 句子改成「我看到 go.mod 等專案檔,
還有 3,355 個原始碼檔」——使用者看得出憑什麼那樣判。

- 既有那條迴歸測試就地擴充到涵蓋模式選擇(不另開平行的一條),
  判準升成「同一棵樹有無版控,收到的檔案集合必須一模一樣」
- 加上三種收法各自仍正確、筆記庫有像產物的資料夾與零星腳本不准翻成專案、
  理由裡不准再出現版控字眼、以及「ingestplan.go 不准再引用 DetectRepoRoot」
  的接線測試(反面同時釘住 #105 在 tidy.go 的正當用途不准被順手拆掉)
- collector/probe_real_manual_test.go:唯讀探針,留著給下一個人重量存量影響

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 23:26:52 +08:00
Leo d1a929e764 併 feat/44-grounding-check(總管已逐項驗過) 2026-08-16 22:58:25 +08:00
Leo 97309b5580 fix(collector): 排除判準不看版控、不誤殺筆記庫、剪掉什麼講得出來(arcrun-rag#104)
票上寫的真兇是錯的。`direct.go` 那一行 `skipDirNames{"system-dev"}` 不是唯一的
排除清單——同一個 Scan 呼叫下面幾行就是 `Plan: plan`,#104 的清單一直都接著。
拿 leo 真實的 `pms` 唯讀跑一輪現行 main:策略 docs-only、送 9 個檔、node_modules 零個。
他 08-16 看到 undici 文件,是因為手上的 daemon 是 v0.18.27,而修法 cc6e500 要到
v0.18.28(08-16 16:13,7f379d0)才被戳版號——那支 commit 自己就寫著
「changelog 停在 v0.18.27,而 collector/ 早已往前走 30 個檔(…cc6e500…)」。

但那個誤判之所以會發生,是因為底下有四個真的缺陷,這一版把它們一起修掉:

① 兩張表分居兩處 ⇒ 讀源碼的人只看得到一張。
   `system-dev` 的保護搬進 IngestPlan(templateOwnedDirNames),
   direct.go 不再手捏第二張清單。判準只剩一個地方。

② 排除規則生不生效,取決於呼叫端記不記得傳 Plan。
   改成 Scan 自己算(Mode == "" ⇒ PlanIngest)。「忘了接」這個失敗模式不存在了。

③ 一張大表把「沒有人會這樣命名」與「這是普通英文字」混在一起,於是**誤殺**。
   實測:一般筆記庫 8 份筆記只送出 1 份(build/樂高作品集、out/外出旅遊、
   vendor/廠商聯絡簿…全被當成建置產物),而且回報「擋掉 0 個」。
   拆成三種理由,強度不同、要求的佐證也不同:
     ① 使用者的 .gitignore 說的(新增 ignorerules.go,git 語法的安全子集)
     ② 名字本身就不是人話(node_modules、__pycache__…)——無條件
     ③ 泛用名(build/dist/out/vendor…)——**旁邊真的擺著專案檔才算**
   🔴 判準一律不看 `.git`(leo 2026-08-16:「你不需要判斷有沒有 git,
   我的 KB 筆記庫也有 git,是否用 github/gitea 追蹤完全沒意義」)。
   `.gitignore` 只讀內容當線索,不拿存在當門檻。
   順帶:鎖定檔(pnpm-lock.yaml…)不是知識——`.yaml` 進白名單後它變成了「知識」。

④ 「排除規則要看得見」只做了一半:整棵剪掉的子樹一個都沒數(pms 實測回報 0),
   而且 Plan/ExcludedByPlan 只有 CLI 讀,daemon(使用者真正走的那條路)拿到就丟。
   新增 ExcludedDirs(路徑+人話理由)+ SyncStatus.FolderPlans 寫進 status.json。

實測(唯讀跑 leo 的 `/Users/youlinhsieh/Documents/tech_projects/pms`):
  139 個文件檔 → 送出 7 個,全是他自己的 README/docs;
  6 個資料夾整棵跳過,每個都講得出理由;node_modules 與授權條款 0 個。

測試:collector 全綠(新增 12 案,含「裸呼叫 Scan 也必須排除別人的套件」、
「不准再有第二張排除清單」的源碼層守門、筆記庫不誤殺、同名看旁邊擺什麼決定);
arcrun-app 全綠。未出貨、未推 main。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 22:48:22 +08:00
Leo 021b3962e2 feat(collector): 卡片上雲前先問「這些宣稱在原文的哪裡」——H7 憑空指稱/H8 宣稱通膨(InkStoneCo#44)
2026-08-16 實測:同一個 220 bytes 的來源檔、同一個模型(workers-ai)、同一個
雲端版本(1.4.46),geek6688 萃出四條全部追得回原文,youlin 萃出八條、其中四條
(Windows/macOS/Linux/每 30 天)在原文零命中——**而那張假卡帶著真實存在的
source_path**。出處在,內容是編的;引用沒有阻止幻覺,反而讓幻覺更可信。

缺的不是更好的模型(機率性,換模型只會降低頻率、讓它更難被發現),
也不是更嚴的提示詞(同一件事的較弱版本,且無法驗證)。
缺的是一道驗證:卡片產出之後、寫進知識庫之前,沒有一步問過「原文的哪裡」。

線畫在哪(grounding.go 檔頭全文):**改寫改的是「怎麼說」,編造加的是「在說什麼」**。
摘要/換句話說/把 EARS 句翻成人話全是正當加工,所以刻意**不比對 CJK 詞**
(原文是英文、卡片是中文時,逐字比對會把忠實翻譯全部誤殺)。
只檢查翻譯與改寫都帶不走的錨點:拉丁專名、兩位數以上的數字。
另加 H8 從數量那一軸看:宣稱數 > 原文斷言數 × 3 = 輸入貧乏卻產出豐富。

分級:兩項都是**軟項**不是硬缺——使用者的知識被丟掉比被標記可疑更糟。
掛載點沿用 direct.go:1206 既有的 LintCard(萃完、POST 前),不新增資料寫入路徑。

順手修:`collector lint <卡> --source <原稿>` 的旗標被 Go flag 靜默忽略,
H6/H7/H8 全跳過還印出漂亮的空結果 `{}`——查幻覺的工具自己靜默不查。

未動(依 SDD 生命週期第 3 條寫成 pending-changes.md P12 等 confirm):
- H6 長度上限對薄原稿是反的,正把**忠實的卡**打成「疑似整段照抄」(實測 469 字 vs 上限 132)
- 軟項沒有出海口:ingest 工作流一個 quality 欄位都沒讀,標了等於沒標

驗證(離線夾具 collector/testdata/grounding/,不依賴任何線上實例):
- 正面:youlin 卡 → H7 抓到 windows/macos/linux/30,H8 抓到 8 條 vs 上限 6
- 反面:geek6688 卡 → H7/H8 零命中
- 邊界:跨語言(英→中)與同語言整段換詞兩組合理改寫 → 零誤殺
2026-08-16 21:38:52 +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 febc6708ef WIP(#44 ⑦⑩):關聯段擷取式不再被 H3 打斷;改名/搬移偵測施工中
⑦(總管已複驗):雲端 parse_card 的邊界正規式 lookahead 是 \n#,
會停在「## 關聯」底下第一個 ### 之前。而規範形卡(ef5e6c5)正是
### 內文知識關係 開頭 ⇒ 除機械補的 part_of 外,關係一條都收不到。
agent 對 youlin 實際部署的 rag_ingest_card POST 真卡:卡上 4 條/落地 1 條。
改成只認下一個 H1/H2 後離線重跑同一段 JS:解析出 4 條。
併同重編 installer 兩份 precompiled workflows(只改 yaml 不重編=新用戶拿不到)。

⑩(施工中):direct.go/manifest.go 的改名搬移下架,尚未驗收。

🔴 尚未送達:這一版還沒部署到 youlin,live after 數字還沒拿到。
不得標 。出貨 CP 步驟①。

未納入本次 commit:collector/cmd/arcrun-app/arcrun-app(20MB 編譯產物)
與兩個探針目錄——那是驗證用的臨時物,不該跟修法一起進版控。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 13:13:59 +08:00
Leo 6c74b97718 feat(collector): workers-ai 路改吃共用契約——免金鑰預設路的卡跟 BYOK 一樣完整(Arcrun#134)
InkStoneCo#44 ④ 只修了 gemma BYOK 路(JSON 契約+wikishape 機械組卡),
免金鑰預設路(多數用戶實際走的)仍吃雲端舊 prompt 的舊格式卡 ⇒ 走預設路
拿到次級知識庫。

修法=契約單一真相源:wikiExtractPrompt(原 gemmaPrompt 改名,因為它已是
兩條路共用)由 daemon 整段帶上雲(request prompt 欄位),雲端只當執行器回
模型原文(response output),解析(parseWikiExtractJSON)與組卡(BuildWikiDoc)
回到本 package 與 gemma 路同一段程式碼——同形不再靠「兩邊要一起改」的叮嚀。

版本歪斜兩向都有路:舊雲端忽略 prompt 回 card ⇒ fallback 走 legacy 落卡
(#60 前綴/不覆蓋保護原封不動,收端 lint 新舊雙軌);舊 daemon 不帶 prompt
⇒ 雲端 legacy 行為不變(對向修法在 Arcrun work/extract-wiki-json-0815)。

測試:+2——①request 的 prompt 必須是 wikiExtractPrompt 本人(不是手抄第二份)
②同原稿+同判斷走兩條路,.wiki/ 產物逐位元組相同(卡+00-INDEX+manifest)。
既有兩則(stub 回 card)自動變成舊雲端 fallback 的守衛。go test ./... 全綠。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 21:26:30 +08:00
Leo 12c2d41886 feat(collector): daemon 產出改成規範形 wiki 卡——1 文件卡+N 原子卡落 .wiki/(InkStoneCo#44 ④)
LLM 只回 JSON 判斷,格式/落點/連結閉合/索引/manifest 全由 wikishape.go 機械組裝:
- 卡形=frontmatter(tags/gloss/created/updated)+← 上層+摘要/重點/實體(帶類型)/
  關聯(內文知識關係/卡片關係/出處)——差距表 #6
- 落點 <節點>/.wiki/、檔名=H1(.wiki 隱藏目錄自身即機器標記,machinemark 例外③)——#7
- 00-INDEX 機械維護:每行五樣照抄 frontmatter;每份原稿必列——#8
- 萃取端就切 1 hub+N 原子卡(gemma 路 prompt 改 JSON 契約)——#9
- 無可萃概念標「空」+理由,上索引不產卡——#10
機械保證對著 wiki-lint.py 寫:斷連結拆殼、index 式句改寫、三元組恰三項、
雙向邊自動補反向、佔用不覆蓋、原稿永不動;同名概念跨文件先消歧(merge 歸第⑤環)。
scan/convert 白名單補 .feature/.yaml/.yml/.org/.rst(規範洞 6);
lint.go 新舊格式雙軌(workers-ai 雲端 prompt 屬 matrix/arcrun 核心,待開票);
direct.go 只送文件卡上雲(takedown 配對鍵不變)、下架收走 .wiki 產物。

實測:真資料夾+真 Gemini → 2 節點 12 卡,wiki-lint 17/17 exit 0,
原稿 sha256 前後不變;go test collector+supervisor+arcrun-app 全綠(新增 9 案)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 21:03:45 +08:00
Leo f233eec9c2 feat(collector): 結構先行——掃描完幾秒內就能問「資料夾裡有什麼」,不等 LLM 不等額度(InkStoneCo#43)
leo 08-15 量測:266 檔真正算的 ~20 分鐘/<$0.5,使用者卻等 ~4 天(多在等
Workers AI 每日額度);而「有哪些檔案/最近改了什麼」本地掃一遍就有答案
(1,335 檔實測 0.254s),卻因 part_of 只在雲端 parse_card(萃取之後)生成
而排同一條隊、撞同一面額度牆。

- collector/inventory.go:Scan() 後、萃取迴圈前(額度冷卻閘之外),把 manifest
  現況做成機械總覽卡(檔案清單/最近改動/目錄 part_of)POST 既有 rag_ingest_card
  ——零 LLM、零新 workflow、既有實例直接受益;自帶同頁名 upsert 防重複
- 冪等:內容 sha256 記 manifest.inventory_hash;失敗 10 分鐘退避(防 t195 同款
  每 5 秒重撞);額度失敗訊息人話化,不裸露 4006/502
- 清單 ≤200 逐檔、關聯 ≤10 條:守 Workers 免費層 50 subrequests 天花板
- 不寫本機檔案、不動使用者原稿

實測(youlin stage yuga3bse,刻意無金鑰=零萃取):總覽卡 200 送達、rag_chat
正確回答「有哪些檔案/最近改了什麼」;重掃不重複(blocks 恆 5、triplets 恆 2);
go test collector+supervisor+arcrun-app 全綠(新增 inventory_test.go 6 案)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 16:39:45 +08:00
Leo 35b113f147 fix(collector+rules): daemon 不再搬用戶既有的卡+第一鐵律改成「照索引走」
## 一、daemon 把 leo 手寫的 16 張決策卡搬走並改名(今天實撞,已還原)

這是 cc6e500(08-14,MigrateCardNames)同一個事故的第二次,連數字都一樣:
那次壓平 cards/autonomy/,今天換成 cards/decisions/,同樣 16 個版控中的檔案變成刪除。
上次只修了 MigrateCardNames,enforceCardMarks 做一模一樣的事卻被漏掉。

真兇在 diffCards 把「新增」與「內容變動」併成同一個回傳值:
萃取 skill 正當地更新一張既有索引卡 → mtime 變了 → 被當成「本次產出的卡」
→ 判定不在 want 目錄 → 搬走改名。**更新一張索引卡,就會害它被踢出自己的桶子。**

第一版修法(在 cards 樹底下就不動)打掛兩支測試,而它們守的是真東西——
#60 前綴防撞名、#105 落 .arcrun-rag 不弄髒版控。改成正確的分界:
  這次新生的 → 歸位+加前綴(兩條保護原封不動)
  本來就在的 → 不管在哪、叫什麼都不准動(可能是使用者手寫的)
測試全綠。

## 二、第一鐵律從「用 grep」改成「照索引走」(leo 2026-08-15 當面糾正)

grep 繞過索引 ⇒ 索引永遠不會被驗證、永遠不會變準 ⇒ AI 再用「它可能不準」
當理由繼續 grep。自我實現。實測:arcrun 的 wiki 64 張卡只有 1 張走得到索引
(63 張機器產的從沒加入任何索引),而沒有人發現——因為 grep 找得到。

改成三步:先照索引走 → 走不到就先把索引缺陷講出來 → 才准 fallback grep。
不全面禁止 grep:索引壞掉那天 AI 會瞎掉且沒人知道。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 11:00:37 +08:00
Claude 129fe79ecb fix(collector): daemon 不再改用戶版控中的檔案,接 repo 只讀整理好的 wiki(#105/#104)
## #105 daemon 會改用戶的檔案(不可逆,先修)

2026-08-14 21:45 實撞:InkStoneCo 在看守清單裡,daemon 回報「已把 1 張舊卡片歸位」,
實際把 system-dev/wiki/cards/autonomy/ 整個子目錄壓平改名,16 個版控中的檔案變成刪除。

真兇是 MigrateCardNames 的一句假設:「那兩個目錄從頭到尾只有 daemon 會寫」。
那句話在 vault 上成立,在 repo 上不成立——system-dev/wiki/cards/ 是 template 的規約
路徑,而 template 就是要裝進開發者自己的 repo,那裡本來就有人家自己的檔案。

- 新增 repoguard.go:`.git` 判準(含 linked worktree),與 #104 共用同一個判準源
- 版控中的資料夾一個檔都不自動動;改成記帳+講給使用者聽,出口是 collector tidy --apply
- Blocked 只算「確定是我們寫的」(帶 arcrun- 標記)——報使用者自己的卡等於發假訊息
- 落卡與工作區改走 .arcrun-rag/(與 vault 同待遇),自帶 .gitignore(*) 讓它對 git 隱形
- 非版控資料夾(含 vault)行為與 #60 第三輪完全一致,不推翻前兩輪的成果

## #104 接上開發 repo 會把上萬個原始檔排進佇列

leo 的規格:「它要辨識這個庫已經有 wiki,那就直接 ingest 了」「只有文件要讀,程式碼不用讀」。

- 新增 ingestplan.go:掃描前先問「這個資料夾是什麼」——all/curated-wiki/docs-only
- 排除靠路徑身分不靠副檔名:依賴、建置產物、templatefs 範本、linked worktree、巢狀子 repo
- 策略與擋掉的數字經 TriggerPayload.Plan 走進 status.json,CLI 走 stderr(排除規則要看得見)
- 子專案自己的 wiki 刻意不收,但一定列出來讓使用者知道去哪裡找
- 與 TemplateOwns(把 system-dev/ 整棵當開發用的)的對撞用身分化解,不拿掉任一條:
  我們代裝的資料夾沒有 .git(舊規則照舊),他自己的 repo 有(收那份 wiki)

## 實測

#105:舊版 daemon 對真 repo 跑一輪 → git status 32 行(16 個 D + 16 個 ??,與 21:45
      撞到的一字不差);新版跑同一輪 → 0 行。另有真 git init 的端到端測試。
#104:造出 leo 那棵樹的形狀(InkStoneCo + products/arcrun-rag + 3 份出貨 worktree,
      967 個文件檔)→ 舊版送 91 個檔(多收的全是 -wt*ship/ 裡的 templatefs 範本、
      benchmark 結果、docs-site 產物),新版送 32 個,全部落在 system-dev/wiki/ 底下。

MachineMark 規約新增唯一例外 IsMachineOwnedRel:.arcrun-rag/ 底下的檔以目錄名為標記
(.gitignore 的檔名是 git 定的,改不得)。迴歸網判準同步換過去。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UUwsLkFEGN8496bZTqhjFq
2026-08-14 14:41:08 +00: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 2f33324d3f fix(collector): #60 監看的是筆記庫底下的子資料夾時,保護整個失效
真正的根因不是「vault 判斷漏了子庫」,是**判斷的方向搞反了**:
前兩輪問的都是「監看根**這一層**是不是 vault」,但 daemon 的產物一律落在
監看根底下——這兩件事只有在「監看根 == 庫根」時才等價,而那正好是前兩輪
唯一測過、也唯一不會出事的擺法。

使用者只要把庫底下的某一層加進監看(`KB/docs`、`KB/pages`、Obsidian 庫裡的
某個專案夾——很自然的用法),DetectVaultType 就回 VaultNone,整套保護退回
一般資料夾模式,卡片落在 `<監看根>/system-dev/wiki/cards/`:那個路徑就在
使用者的 graph 裡面,而且看得見,Logseq/Obsidian 每一張卡都收編成一頁。
前綴(第二輪)只擋得住撞名,擋不住「多出一堆機器頁」。

改法:把「這一層是不是庫」與「我寫的東西會不會落進誰的庫」拆成兩個判準。

- vault.go:新增 DetectVaultContext(往上找到最近的庫根)與 VaultDirUnder
  (往下擋:寫入目標會不會踩進子庫)。DetectVaultType 一字未改,繼續與
  install.sh 對齊——往上找用較嚴的判準(logseq/ 要有 config.edn 或
  journals//pages/ 佐證),因為那是替使用者猜、而且一次猜好幾層。
  停在家目錄與檔案系統根,避免 `~/logseq` 這種常見資料夾把整個家目錄判成庫。
- extract.go:cardsRelDirFor 改用 DetectVaultContext。
- safewrite.go:落卡前過 ensureWritable 機械閘——目標踩進子庫就中止,
  不靜靜寫進去。今天不會觸發,它防的是以後新增的寫檔點。
- tidy.go:收拾判準從「有沒有帶標記」擴充成「位置對不對 + 有沒有帶標記」,
  舊版留在看得見位置的卡會被搬進隱藏目錄;MigrateCardNames 每輪自動做,
  使用者不必下任何指令。報告多一個 VaultRoot,說清楚是誰的庫。

leo 派工單上的線索(庫在監看根**底下**)實測不成立:產物一律錨在監看根,
不會落進子庫。但那個「本來就沒破」原本沒有任何機制保證,所以照樣把兩種
格式的子庫情境永久寫進測試,加上 ensureWritable 當第二道保險。

驗證缺口(票上第 6 條):第二輪的足跡測試方向是對的,漏的是**觀測窗**——
snapshotTree 只拍監看根,而災情發生在監看根外面、庫裡面;且 fixture 只有
`root := vault` 一種擺法,測試與被測程式犯了同一個假設,所以永遠是綠的。
vault_subdir_test.go 把快照邊界改成筆記庫,並把「監看根與庫根的關係」升格
成測試維度(庫在上/庫在下/庫就是它/沒有庫 × Logseq/Obsidian)。

全程只用 t.TempDir() 與 mktemp -d;沒碰任何真實筆記庫、沒重啟任何 daemon。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 18:23:49 +08:00
Leo 5322103459 fix(collector): 修好 #60 的編譯——測試用的 sortedKeys 與 convert_table.go 撞名
上一批把修法寫完了但沒編譯過:vault_footprint_test.go 自己定義了一支
sortedKeys,而 convert_table.go 早就有同名的(吃 map[string][]byte),
同一個 package 裡不能重名。改名成 sortedFootprintKeys,並順手把本分支
碰過的三個檔 gofmt 過。

驗收(實跑):
  bash collector/verify-60.sh  →  全部通過(五關)
  go test -count=1 ./...       → ok collector 9.6s / ok supervisor 12.2s

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 15:07:55 +08:00
Leo 5a140faf1f fix(collector): 機器寫進筆記庫的檔案一律帶前綴,不再跟使用者的頁面撞名(arcrun-rag#60)
leo 2026-08-12:「我的 Logseq 又被覆蓋⋯⋯**不只是加上 journal,可能所有的檔案都加一個前後綴,比如「wiki」**。」

不是資料被蓋掉,是機器產出用了跟他一樣的命名空間(status.md、日期檔那些)
⇒ 他打開資料夾分不出哪些是自己的。**心理上的覆蓋跟實際覆蓋一樣糟。**
2026-08-12 14:57:32 +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 2cdd5bd4bc merge: 額度卡與雲端連線兩則假訊息改口(arcrun-rag#59)
總管 2026-08-11 審過後併入:
- 額度訊息不再指向無效的出口(換模型解不掉被向量化吃掉的額度)
- 多帳號時雲端狀態不再永遠停在零值 false
- 含對照修改前程式碼會 FAIL 的回歸測試
- 總管實跑 go test ./...:collector 全過

◐ 用戶手上仍是舊訊息:daemon 未重打、未出貨
2026-08-11 13:22:39 +08:00
Leo 4cbc7ebe11 fix(arcrun-rag#59): 額度卡與雲端連線兩則假訊息改口
leo21c 畫面同時說「今天已經幫你整理了 0 份」與「額度用完,可以換一個模型,
或升級 Cloudflare」——一份都沒成功,額度其實是被同帳號正在做的向量化
(嵌入固定走 Workers AI,與萃取共用同一份每日免費額度)吃光的,換萃取模型
救不了。改法:buildQuotaNotice 在 dailyCount==0(本輪一份都沒吃到額度就先
撞牆)時換一套出口/保證句——不再建議換模型、不再無條件承諾「明天會自動
接著跑」,只留「升級 Cloudflare」這個結構上真的有效的出口;dailyCount>0
(單純量大用完)維持原三句話不動。

同批修另一則真因已被頂層 wiki 鎖定的假訊息:leo 三個帳號 /health 全部 200,
畫面卻顯示「沒連上雲端」——direct.go 寫 status.json 頂層 cloud_check_ok 時
以前只有剛好一個帳號才會填,2+ 帳號(leo 的常態)時恆為 Go 零值 false。
改成不論帳號數,任一帳號連得上就標頂層為 true。

兩者都有回歸測試:對修改前的程式碼跑會 FAIL(多帳號 CloudCheckOK 案例已
用 git stash 實測驗證),修完後 PASS。額度卡文案另外用真正的 frontend/dist
+ 假 window.go 灌 leo21c 現場資料,Claude Browser 實際開起來看過,確認新
文案正確渲染、不再出現「可以換一個模型」、console 無紅字。

未做:兩者都只在原始碼層修好,尚未重打 daemon bundle 出貨(0.18.25 用戶
手上還看不到);頂層 cloud_check_ok 的實際消費端(除已知的舊版 tray)未
完全追出,留給下一輪核實。詳見 system-dev/docs/3-specs/daemon-beta/tasks.md
t216/t217。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 12:28:49 +08:00
Leo f10d10747c fix(collector): 萃取產物加 vault 辨識+落卡不再無條件覆蓋(arcrun-rag#60)
事故:daemon 完全沒有 vault 辨識,把萃取卡片寫進使用者的 Logseq vault
(system-dev/wiki/cards/ 對一般資料夾沒事,對 vault=憑空多出頁面,
2026-08-10 leo 實撞:25 個簡體字卡片污染 ~/Documents/KB)。

修法(照票上指示搬現成的,不重新設計):
- collector/vault.go:DetectVaultType/IsVault,判準逐條抄自
  system-dev-template/scripts/install.sh:209-221(先查 logseq/、再查 .obsidian/)。
  已用同一批 fixture 資料夾跑過 install.sh 與這支 Go 版,五種情境(logseq/
  obsidian/一般資料夾/兩者皆有/空資料夾)IS_VAULT 判斷逐一比對一致。
- extract.go:cardsRelDirFor() 依 IsVault 決定卡片相對路徑——非 vault 不變
  (system-dev/wiki/cards/),vault 改落 .arcrun-rag/wiki/cards/(點開頭隱藏
  目錄,Logseq/Obsidian 預設不掃描,跟 daemon 自己 scan.go 的隱藏目錄跳過規則
  一致)。extract_workersai.go/extract_gemma.go/direct.go 的落卡與下架清除
  都改用這個函式,三處對同一個 absRoot 保證同一個答案。
- safewrite.go:safeWriteCard() 取代兩處無條件 os.WriteFile——目標已存在且
  內容不同就先備份成 <dest>.bak-<unixnano> 才覆寫;內容相同則不動(不產生
  垃圾備份);備份失敗就整個中止,不無聲蓋掉使用者機器上已有的東西。

驗證(見 PR/commit 說明附的實測輸出):
- 用 fake Logseq vault fixture 重現舊行為(卡片確實落在 system-dev/wiki/cards/),
  再用同一份 fixture 驗新行為(卡片改落 .arcrun-rag/wiki/cards/,vault 根目錄
  非隱藏 .md 數量不變、journals/ 原稿位元不動)。
- 故意放同名既有卡片,跑完既有內容被備份、新內容確實寫入,未無聲遺失。
- 非 vault 既有測試(extract_gemma_test.go 原有三支)全數不動照過,確認
  一般資料夾行為零改變。

範圍外(留給下一輪):ExtractWithClaude(extract.go 的 claude 路)與其
templatefs/.claude/commands/rag-extract-file.md 技能檔仍硬寫 system-dev/wiki/cards/,
但這條路目前在 direct.go 的 RunDirectOnce 是不支援狀態(cfg.Extractor 只認
workers-ai/gemma),非本次事故的作用路徑,故未動。

不影響:leo 機器上的 daemon(未重啟、未重新打包);此修復要生效還要
①總管審過併 main ②重新打包桌面版裝上他機器 ③總管用新二進位實測,
三件缺一不可(見 issue #60 leo 的重啟條件)。
2026-08-11 12:12:51 +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 63bea13f21 feat(daemon-beta B2): 萃取品質 lint(H1–H6)重新接到現行 direct.go
Gitea #24:分支 work/b2-quality-lint-0726(2f3e9ec)躺在 main 外兩週,
今天重新整合進現行 direct.go(原分支落後 main 130+ 筆,direct.go 本身
差 667 行,需要真正的整合工作而非硬併,見 wiki status.md 與頂層
daemon-beta/tasks.md 已記載的殘項)。

- collector/lint.go/lint_test.go:package main → package collector
  (main.go 在 4a26856/v0.18.9 已從兩支執行檔併成一支,整個 collector/
  目錄已改名 package collector;分支停在改名之前)。
- collector/main.go:func main() → func Run(args []string) int
  (同一次重構),新增 case "lint" 呼叫 runLint。
- collector/direct.go:DirectConfig.LintStrict 欄位+extractor 萃完、
  POST rag_ingest_card 前掛 LintCard 閘(硬缺→不送標 rejected;
  軟項→照送帶 quality:low+quality_warnings);CLI --strict 旗標。
- 既有測試共用的 cardFixture(direct_extract_test.go)/direct_pacing_test.go
  三處 inline 卡片,從「兩段最簡卡」補成 B2 合格四段卡——不是新增例外,
  是 lint 正確地在做它的事,舊卡片本來就不合格。

驗證:go build/vet/test ./... 全綠;另以真實 PDF(docs/onboarding/封測說明.pdf,
2MB 二進位)跑過完整 direct.go 同步鏈,證實送進 Gemini 的 prompt 是
5,071 字乾淨中文(0 個控制字元/NUL),不是原始位元組——ConvertToText
(t73)已完整解決 900b895 想止血的問題,故該分支的 looksLikeText
二進位止血、Windows 交叉編譯腳本判定作廢,不併。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 14:26:21 +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