iT邦幫忙

2026 iThome 鐵人賽

DAY 22
0
AI Engineering

一天一個 PR,讓我告訴你嵌入式開發的殘酷:如何為 AI Agent 建立可信的工程閉環系列 第 22

Day 22 - 新 Repo 明明能跑,怎麼還有一半住在舊家?:Cortex 正式拆分

  • 分享至 

  • xImage
  •  

系列說明 >> 本系列大部分 PR 來自私人或公司專案,部分程式碼、環境設定與實作細節不便公開。

我只能保證文中提到的 PR 與問題皆為真實案例,但會經過必要的匿名化與內容調整。

本系列主要分享問題如何被發現、背後的思考方式,以及解法如何形成,不會深入討論完整實作與部署細節,敬請見諒。

Today’s Change

  • Base Repo: paulsha-cortexpaulshaclaw

  • Change Ref: Cortex PR #3-feat: 平移 deck 與 monitor 進 paulsha-cortex+paulshaclaw PR #235-feat!: 主 repo 遷移刀切成 paulsha-cortex operator shell

  • Issue: D21 已讓 Persona/Coordinator/Control 在 Cortex 形成可獨立啟動的 Runtime Nucleus,但 Deck/Monitor 仍留在 paulshaclaw,舊五個 Runtime Package、Manager Service 與既有 Import Path 也尚未完整退役。新 Repo 能跑,不代表 Workflow Authority 已經完成轉移。

  • Root Cause: 拆分只做了新側 Bootstrap,沒有同時完成剩餘能力遷移與舊側退役;「保留 CLI 相容」又被誤解成「保留第二份 Runtime 實作」。Source 可以複製,唯一 State Owner、常駐 Manager 與 Service Cutover 卻不能靠共存解決。

  • Solution:deck / monitor 補入 Cortex,使 persona / coordinator / control / deck / monitor 五個 Runtime Package 全部在新側成立;再從 paulshaclaw 刪除舊五包,既有 psc coordinator|deck|monitor 只保留轉送 Cortex Public CLI 的 Thin Shim。Runtime Cutover 則以常駐 Cortex Manager 接手,並先 Stop+Disable 舊 Manager,再啟動新 Service。

  • Evidence: Cortex PR #3 記錄 452 tests pass,Fresh-install 可執行 cortex deck compile ...cortex monitor --once;paulshaclaw PR #235 記錄 447 tests pass、五個 Package 刪除、舊 Import Grep 清零與三組 Alignment Tests 綠。對抗審查另抓出 one-shot tick 不能取代常駐 Manager,以及新 Service 啟動前必須先停用舊 Service 兩項 High Finding。這些證據支持 Source 與 Authority Cutover Mechanism 已落地,不代表長時間 Production Soak 已完成。

Target


我都準備寫「拆分完成」了,小re先問 Deck 在哪裡

上一篇最後,Cortex 已經可以 Fresh Install。

cortex --help 也叫得出來。

Persona、Coordinator、Control 都有自己的 Path、CLI、Hook 與 Service 啟動面。

我看著測試結果,準備在紀錄上寫:

Cortex 拆分完成。

小re先問:

「Deck 在哪裡?」

「……paulshaclaw。」

「Monitor 呢?」

也是。

這時我才發現,新 Cortex 雖然能啟動,卻還得回舊 Repo 拿兩樣東西。

Deck 前一篇才剛出場,負責把 Card/Combo 編成 Runtime Specs。

Monitor 則負責週期整理 Project 與 Runtime 的狀態,讓我知道哪裡還活著、哪裡很久沒動、哪裡需要被看見。

我原本想把 Monitor 也算進小ma。

小ma很快撇清:

「我負責判斷 Transition 能不能過。」

「不是每天巡一遍誰還沒回報。」

小re又問:

「Monitor 看見卡住,可以自己改 State 嗎?」

「不行。」

「那『看見』跟『決定』就不是同一個責任。」

到這裡,三個責任才真正排開:

Deck
→ 定義並編譯工作

Coordinator
→ 推進工作

Monitor
→ 持續觀察工作是否還在前進

新 Runtime 如果還得回 paulshaclaw 拿工作定義、借眼睛看自己活得怎樣,這個拆分顯然還沒拆完。

老Go看了一眼。

「新家可以開門。」

「只是每天還要回舊家拿工單、看電表。」

有道理,「拆分完成」。

Target

最後兩包搬進 Cortex,五個 Runtime 責任才真的站到同一邊

Cortex PR #3 把 Deck/Monitor 補進新 Repo。

搬完後,新側才第一次完整擁有:

persona
coordinator
control
deck
monitor

小pa照例先問責任:

「這五個東西是因為名字相近才搬在一起?」

「還是因為它們共同決定 Workflow 怎麼被定義、執行與觀察?」

是後者。

Persona 約束 Agent 以什麼角色工作。

Deck 把可重用 Workflow 編成 Specs。

Coordinator 依 Dependency 推進。

Control 讓 Operator 與 Runtime 交換 Request/Status。

Monitor 則持續整理 Runtime 現況。

它們不一定每次都直接 Import 彼此。

但它們共同構成同一套 Workflow Runtime。

Fresh-install 也實際跑過:

cortex deck compile feature-oneshot --task smoke
cortex monitor --once

到這裡,新家總算不是只有 Coordinator 一個人坐在客廳,其他人每天跨 Repo 通勤。

小bu看著新側五包都齊了,很快宣布:

「這次真的搬完。」

小re沒有回答。

它切回 paulshaclaw 看了一下。

同樣五個 Package,還在。


小bu說舊的先別刪,小pa只問我要相容哪一層

我對「舊的先留著」一向很難拒絕。

舊路躺在旁邊,心裡總覺得比較有底。

小bu提出的方案也非常熟悉:

「舊 Package 先保留,CLI 慢慢改指向 Cortex。」

「有問題還能立刻切回來。」

小pa把 Cutover Plan 往回翻了一頁。

「你要保留的是哪一種相容?」

「大家已經習慣的 Command。」

例如:

psc coordinator
psc deck
psc monitor

「那為什麼舊的 Persona、Coordinator、Control、Deck、Monitor 也要繼續活著?」

……對喔。

Command Name 是 Operator Interface。

Package 實作卻會讀 Spec、寫 Status、推進 State。

兩者不是同一件事。

這時真正需要的是 Thin Shim:保留舊指令名稱,但自己不再做決策,只把參數轉交 Cortex 的薄轉接層。

所以 PR #235 做了一刀很直接的切割:

刪除 paulshaclaw/persona
刪除 paulshaclaw/coordinator
刪除 paulshaclaw/control
刪除 paulshaclaw/deck
刪除 paulshaclaw/monitor

既有 psc Command 留下來,但只做:

psc command
→ cortex public CLI

小re沒有先看 README 裡寫了多少次 Deprecated。

它直接查:

「舊 Package 現在還能不能 Import?」

不能。

「舊 Command 會不會再自己改 State?」

也不會。

這才叫 Shim。

小bu看著五個被刪掉的目錄,還是有點不安。

「完全不留第二份,比較難 Rollback 吧?」

老Go問:

「你要留的是回到舊版本的方法。」

「還是另一個會繼續改 State 的人?」

Rollback 可以靠 Version 與 Install Artifact。

不能靠兩套 Runtime 同時活著。

門牌可以相容,屋主不能兩個,一山不能容二虎。


五包都刪完了,小bu又拿一次 tick 當常駐 Manager

Source Tree 終於乾淨,我原本以為剩下只是啟動問題。

沒有 Systemd 的環境,需要一條 Fallback。

小bu很快找到:

cortex coordinator tick

tick 是一次性的推進命令:掃一輪 Request/Specs,處理完就退出。

它跑完後 Exit 0。

小bu指著結果:

「Manager 有跑。」

小ma問:

「五分鐘後才進來的 Request 呢?」

沒人處理。

它只能證明這一刻有人來看過,不能證明接下來一直有人負責讀 Request、持有 Lock、推進 Lifecycle。

實際情況會變成:

啟動 → tick 一次 → process exit
五分鐘後送 Action → Request 留在原地

前端還是可以顯示「已送出」,後面則已經下班了,射後不理!!!

老Go看了一眼。

「這不是常駐 Manager。」

「這是早上刷卡後,立刻消失的同事。」

所以 Fallback 也必須啟動 Manager Daemon:常駐在背景,持續接 Request 並推進 Workflow 的 Process。

小bu還想爭取:

「那每分鐘跑一次 tick 呢?」

小re問:

「你是在做 Manager,還是在自己重寫一個更難除錯的 Timer?」

這條路先收回來。

Target


新 Manager 開始值班,舊 Timer 卻還會定時醒來

新 Cortex Manager 能常駐後,我又提出一個看起來更穩的順序:

先 Start 新 Service
確認正常
再 Stop 舊 Manager

小bu覺得非常安全。

「至少中間不會沒人顧。」

小re問:

「中間那段時間,誰是 Authority?」

「新的。」

「舊 Timer 下一次醒來時,它知道嗎?」

不知道。

Timer 是 Systemd 週期喚醒舊 Manager 的排程;只要它還 Enabled,舊 Runtime 就可能再次起來讀 Request、寫 Status、推進 Lifecycle。

我嘴上宣布新側接管,舊 Timer 不會因此自動產生退休意識。

這時真正的 Cutover——把正式控制權從舊 Runtime 交給新 Runtime——不能只看新 Service 有沒有啟動。

小pa把順序重排:

Stop old paulshaclaw manager
        ↓
Disable old timer / service
        ↓
確認舊 Authority 不再活動
        ↓
Enable + Start Cortex manager
        ↓
驗證 lock / request / runtime path

這個順序會留下短暫沒有 Manager 的空檔。

小bu皺了一下眉。

「中間不是會沒人管?」

小ma回答:

「可以知道哪一段沒人管。」

「不能接受兩個人都說自己在管。」

先開新、再慢慢關舊,留下的則是兩個 State Owner 同時存在。

Firmware 可以保留舊 Image 做 Rollback。

CPU 不會因為你想保險,就同時從兩個 Reset Vector 往下跑。

老Go說:

「Rollback 是出事時能回去。」

「不是兩邊先各跑各的,看誰比較像真的。」

這次舊 Timer 真的被 Disable 了。


拆分完成後,paulshaclaw 還在,只是不再有第二顆腦

五個 Runtime Package 搬進 Cortex。

舊五包從 paulshaclaw 刪除。

CLI 只留 Thin Shim。

舊 Manager Stop+Disable,新 Cortex Manager 常駐接手。

到這裡,Cortex 拆分才第一次完整。

paulshaclaw 沒有因此變成空 Repo。

它收斂成 Operator Shell:讓人查 Status、送 Action、看例外,但不再持有第二份 Workflow State。

小bu盯著舊 Command。

「所以 psc coordinator 還能用?」

「能。」

「那它還是 paulshaclaw 的 Coordinator?」

小pa回答:

「入口是。」

「責任不是。」

小re確認舊 Import 已清零,小ma確認 Transition 只剩一個 Owner。

老Go看著那幾個還能照常輸入的 psc Command。

「招牌可以不換。」

「裡面的人已經搬走了。」

這次總算不是比喻完,大家繼續各跑各的。

我真的把舊 Manager 關了。

Target


今天證明的是 Cortex 拆分完成,不是新家從此不漏水

這一輪留下的證據包括:

Cortex PR #3
→ 452 tests pass
→ fresh-install deck compile
→ fresh-install monitor --once

paulshaclaw PR #235
→ 447 tests pass
→ 5 packages deleted
→ old imports grep-zero
→ alignment tests green

對抗審查另外抓出:

  • one-shot tick 不能替代常駐 Manager。

  • 新 Manager 啟動前,舊 Service 必須先 Stop+Disable。

這些可以支持:

五個 Runtime Package 已集中到 Cortex;paulshaclaw 只保留 Operator Shim,Runtime Authority 已完成 Cutover。

它不能支持:

Cortex 從此不會遇到 Service、Lock、Deploy 或 Recovery 問題。

拆分完成,只代表下次出問題時,不必先確認到底是哪一套 Manager 動了手。

我正準備收工,另一隻 Agent 看著刪除清單問:

「舊 Package 為什麼不能先留著相容?」

小bu看了它一眼。

這題今天才吵完。

小re剛查過。

小ma剛擋過。

小pa連 Cutover Plan 都重排了。

但換一隻 Agent,前面的學費還是沒有自動到帳。

老Go看著完整紀錄。

「執行的腦搬完了。」

「記性還留在聊天記錄裡。」

下一篇,原本住在 paulshaclaw 裡的工程記憶,也該有自己的家了。

Have a nice day.


上一篇
Day 21 - 工單都寫好了,Workflow 卻還不想動:Coordinator推著它走
下一篇
Day 23 - 昨天才踩過的坑,換一隻 Agent 又從頭問:工程經驗到底存在哪裡?
系列文
一天一個 PR,讓我告訴你嵌入式開發的殘酷:如何為 AI Agent 建立可信的工程閉環28
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言