thread-master/AGENTS.md

18 KiB
Raw Blame History

Agent Handoff Notes

這個資料夾是新的巡樓後端核心,請優先維持乾淨邊界,不要把舊 Next.js 或 template-monorepo 的業務包袱搬進來。

核心原則

  • 全系統時間一律 UTC+0;寫入 Mongo / API 的時間欄位一律 unix nanosecondsint64)。排程的 timezone 只用於 cron 解讀與下發 payload不作為儲存時區。
  • 複製模式,不複製舊業務。
  • logic 做 API 編排,model/usecase 做可重複使用能力。
  • provider adapter 不讀 setting、不碰 Mongo、不知道 HTTP。
  • setting 是通用 key-value model不依賴 AI 或其他業務。
  • token / API key 第一版每次 request 帶入,不寫入 config。
  • SSE contract 由本服務 normalize前端不要讀 provider 原始 chunk。
  • JSON API 必須使用 code/message/data/error envelope 與 SSCCCDDD 錯誤碼。
  • 列表 API 必須使用 page/pageSize query並在 data 回傳 pagination/list
  • Job 狀態轉移必須使用 guarded/conditional update不要在 API/worker 直接裸 Update 覆蓋 job 狀態。
  • Redis job lock 的 value 是 workerIDrelease / refresh 必須檢查 owner長任務必須 heartbeat。
  • Auth 目前是 native email/password + JWT不包含 OAuth / OTP / MFA / Zitadel。不要為了相容 template-monorepo 把重依賴搬進來。
  • AI provider token 與會員 JWT 是兩種不同 tokenAI token 每次 request header 帶入,會員 JWT 由 /api/v1/auth/* 簽發。

設計文件

  • docs/job-system-plan.md:通用 job system 規劃,包含 template、run、schedule、Redis queue/lock、取消語意與 API 草案。
  • docs/scan-placement-plan.md:海巡獲客(流程 B— 知識圖譜、Brave 擴展、雙軌爬取7 天重點 / 30 天補充)、產品匹配、島民交接。

新增 API 流程

  1. 修改 generate/api/*.api
  2. 優先使用 make gen-api 重新產生 handler/logic/types。
  3. 若手寫 handler仍需遵守 response.Write 與 validator 流程。
  4. SSE endpoint 不使用 response.Write,直接輸出 text/event-stream
  5. 更新 README.md 的 API 與架構說明。

Response / Error Code

錯誤碼格式是 SSCCCDDD

SS  = scope
CCC = category
DDD = detail

目前 scope

10 = Facade
32 = Setting
33 = AI
34 = Job
35 = Auth
36 = Member
37 = Permission

建立錯誤時使用:

errs.For(code.AI).InputMissingRequired("缺少 AI provider token")
errs.For(code.Setting).ResNotFound("找不到設定")
errs.For(code.Job).ResInvalidState("job state changed; update rejected")
errs.For(code.Auth).AuthUnauthorized("missing bearer token")

不要直接手寫 33104000 這種數字,也不要回傳裸 error 給 handler 後讓使用者看到內部錯誤。

Pagination

列表 API 使用:

?page=1&pageSize=10

response

{
  "code": 102000,
  "message": "SUCCESS",
  "data": {
    "pagination": {
      "total": 100,
      "page": 1,
      "pageSize": 10,
      "totalPages": 10
    },
    "list": []
  }
}

page/pageSize 必須是 server 正規化後的值。不要使用 offset/limit/items

新增 Model 流程

模組放在:

internal/model/<module>/
  domain/entity
  domain/repository
  domain/usecase
  repository
  usecase

依賴方向:

handler -> logic -> model/domain/usecase
model/usecase -> model/domain/repository
model/repository -> Mongo / Redis

不要讓 logic import model/<module>/repository

Auth / Permission 擴充

目前已接:

POST /api/v1/auth/register
POST /api/v1/auth/login
POST /api/v1/auth/refresh
POST /api/v1/auth/logout          # requires member JWT
GET  /api/v1/members/me
PATCH /api/v1/members/me
GET  /api/v1/permissions/catalog
GET  /api/v1/permissions/me

Auth matrixinternal/handler/routes.go

路由 需要會員 JWT
GET /api/v1/health
POST /api/v1/auth/register/login/refresh
POST /api/v1/auth/logout 是(Authorization
GET /api/v1/ai/providers
POST /api/v1/ai/chat/stream/models 是(X-Member-Authorization+ provider tokenAuthorization
/api/v1/members/*/api/v1/permissions/me 是(Authorization
/api/v1/permissions/catalog/api/v1/settings/*/api/v1/jobs*/api/v1/job/* 是(Authorization

規則:

  • 保護路由用 internal/middleware.AuthAuthorization: Bearer <access_token>AI 變更路由用 middleware.MemberAuthX-Member-Authorization),因 Authorization 保留給 provider API key。
  • logic 從 authctx.ActorFromContexttenant_id / uid
  • 不要在 handler 直接 parse JWTtoken 驗證集中在 model/auth/usecase
  • 密碼只存 bcrypt hash不回傳、不寫 log。
  • members.roles 第一版是簡化 role key。正式 RBAC 可逐步補 roles collection但不要破壞 role_permissions 的 tenant + role_key contract。
  • Auth.DevHeaderFallback 只給本機開發,正式環境應關閉。

AI Provider 擴充

新增 provider 時:

  1. internal/model/ai/domain/enum 新增 provider id。
  2. internal/model/ai/provider 新增 adapter。
  3. internal/model/ai/usecase registry 註冊 provider 與 models。
  4. 確保 adapter 回傳統一 StreamEvent
  5. 不要改 logic/ai 的 SSE 格式。

Job Worker 擴充

新增 job step 時優先註冊 runner handler

runner.RegisterStepHandler("analyze_8d", func(ctx context.Context, step job.StepContext) error {
    if err := step.Heartbeat(ctx); err != nil {
        return err
    }
    // do work, check cancel via job usecase if needed
    return nil
})

規則:

  • Handler 不要直接操作 Mongo / Redis透過 job usecase 更新進度、完成、失敗或取消。
  • 長任務每個 checkpoint 呼叫 StepContext.HeartbeatRefreshRunLock
  • 收到 cancel signal 後呼叫 AcknowledgeCancel(jobId, workerID),不要自行把狀態改成 cancelled
  • release lock 時必須帶 workerID;不要新增無 owner 的 release helper。

前端設計規則(web/

巡樓 Console 前端在 haixun-backend/web/,視覺為沉穩田園巡檢台(動森感:天空、雲朵、奶油卡片、青綠 brand不是任天堂 UI 複製)。

字體固定 Inter + Taipei Sans TC圖示僅 AcIcon / AuthDecor 內原創 SVG 線條圖。禁止 emoji、貼圖 JPG、咖啡色木質頂欄、Nook / 任天堂命名。樣式集中在 index.css--hx-* token + hx-* / ac-* / auth-* class

不要把舊 Next.js / template-monorepo UI 搬進來,也不要引入重型 UI 框架。

視覺架構(登入前後共用)

全頁背景 .hx-scene          灰藍天空 → 淡草地單一漸層(淺/深各一套,見 index.css
裝飾層 SceneDecor           雲朵(多朵緩動)+ 淡光暈 + 小葉子;登入與 Layout 共用
奶油卡片 .auth-ticket       2px line 邊框、圓角 2rem、surface 底、可選 .ac-dialog-texture 點陣
頂部品牌列                  圖示 .auth-ticket-iconbrand-soft 底)+ ink 標題;不用獨立色塊 ribbon
主內容                      表單或 Outlet內文頁用 PageTitle / Card綠色 .ac-title-bar 僅內容區小標)
桌面側欄 .ac-pocket-device  掌上終端外框PATROL PAD 狀態列;固定尺寸 + .ac-pocket-scroll 內捲
手機 .ac-dock               底部最多 4 格 +「更多」sheet
區域 元件 關鍵 class
未登入 AuthShell + LoginPage / RegisterPage hx-scene auth-scene auth-ticket auth-welcome auth-shell-form
已登入外殼 Layout hx-scene ac-app-shell ac-app-header auth-ticket ac-app-main-inner
背景裝飾 AuthDecor.tsxSceneDecor hx-scene-deco auth-cloud--*
品牌小圖 AuthTicketIcon auth-ticket-icon(小屋+樹,原創 SVG
側欄導覽 Layout + navApps ac-pocket-device ac-app-tile
手機導覽 MobileBottomNav ac-dock

登入頁刻意不做的事:上方不要獨立大色塊 header表單上方不要再放 ac-title-bar「登入」大牌(品牌已在 auth-welcome)。註冊頁同理可省略重複大標。

已淘汰、勿加回ac-island(改用 hx-scene)、ac-wood-bar / 咖啡色木質頂欄、public/ac/ 貼圖、Nook Phone 文案。

技術棧與指令

web/
  src/
    api/          # API clientenvelope、JWT refresh
    auth/         # AuthContext
    components/   # Layout、AuthShell、AuthDecor、ui、ThemeToggle、MobileBottomNav、AcIcon
    theme/        # ThemeContext淺色 / 深色)
    pages/        # 路由頁面
    lib/          # acAssets導覽 icon key、jobStatus 等
  index.css       # 設計 token 與場景樣式唯一來源
make web-dev      # dev server :5173proxy 到 :8890
make web-build    # tsc + vite build

字型

語言 字型 載入方式
繁體中文 台北黑體 Taipei Sans TC npm taipei-sans-tc,在 index.css @import Regular + Bold
英文 Inter(與 simular.co 相同Google Fonts 免費) web/index.html link

規則:

  • body / 中文標題:Inter + Taipei Sans TC 混排(--font-sans)。
  • 純英文裝飾字導覽副標、Hero 小字):加 class display-en,使用 --font-en
  • 中文 line-height 維持 1.7+;不要用過細字重當標題(標題用 font-bold / font-black)。
  • 只載入 Taipei Sans TC Regular + Bold,不要載入 Light避免小字過細。
  • 不要改回 Noto Sans TC也不要手寫 #333 這類裸色碼當主色。

對比度與字級

  • 內文、表頭、表單 label、卡片說明優先 text-ink / text-ink-secondary不要text-muted 當主要閱讀文字。
  • text-muted 只給次要提示筆數、hint、placeholder 用 text-subtle)。
  • 表單輸入字級 15pxtext-[15px]),輸入框底用 bg-surface 白底,確保與背景拉開。
  • 淺色 muted#5a6578、深色約 #b8c4d6;改色時以「小字仍可舒適閱讀」為準,不要回到 #94a3b8 那種淡灰。

主題(淺色 / 深色)

  • ThemeProvidersrc/theme/ThemeContext.tsx)包住 App偏好存 localStorage keyhaixun.themelight | dark)。
  • index.html 內嵌 script 在 React 載入前設定 data-theme,避免閃爍。
  • 所有顏色必須走 CSS 變數 --hx-*,再映射到 Tailwind @themebg-canvastext-brand 等)。
  • 切換按鈕用 ThemeToggleac-btn-secondary 樣式);Layout 頂欄與 AuthShell 右上角都要有。
  • 禁止在元件裡寫死 bg-slate-*text-emerald-*bg-amber-* 等 Tailwind 預設色;語意狀態用 text-success / text-warning / text-dangerjobStatus.ts 的 badge class。

淺色:低飽和灰藍天空 + 灰綠草地 + 奶油 surface + brand 青綠;深色:黃昏低對比、同一套 token 自動切換。頂欄與卡片內品牌區都用 surface / ink / brand,不要再用木色 #c4a882 當 header 底。

場景與卡片 class維護時對照

Class 用途
.hx-scene 全頁天空→草地漸層(登入 + 已登入根節點)
.hx-scene-deco / SceneDecor 背景雲、光暈、葉子(pointer-events: none
.auth-ticket 奶油主卡片外框(登入卡、已登入主內容區)
.auth-welcome 卡片內品牌列:圖示 + 標題 + 一句 tagline底部分隔線
.ac-app-header 已登入 sticky 頂欄:半透明 surface + blur木色
.ac-title-bar 內容區綠色小標題(裝置色漸層);用於 PageTitle 等,用於登入頁表單上方大牌
.ac-pocket-device 側欄掌上終端;--pocket-width28rem--pocket-screen-height 固定,內容在 .ac-pocket-scroll 捲動
.ac-app-tile / .ac-dock App 格導覽、手機底欄
.auth-shell-form 登入/註冊表單放大字級(僅 auth 頁)

側欄標示用 PATROL PAD 等中性英文裝飾字(display-en);圖示僅 AcIcon SVG。

色彩 token語意命名

開發時只用這些 Tailwind class值定義在 web/src/index.css

Token 用途
canvas 全頁背景
surface / surface-muted 卡片、輸入框底
ink / ink-secondary / muted 主文 / 次文 / 輔助
line 邊框
brand / brand-hover / brand-soft 主 CTA、active 導覽、連結 hover
glow 裝飾色塊(.glow-blob-alt
success / warning / danger(含 *-soft 狀態、錯誤、Job badge

主按鈕一律 Button variant="primary"bg-brand,不要用全黑按鈕。

圓角與陰影

--radius-sm   0.75rem   小元素、code
--radius-md   1.25rem   Input / Textarea
--radius-lg   1.75rem   Card
--radius-xl   2.25rem   Hero、QuickLink、StatCard
--radius-pill 9999px    Button、Badge、導覽 pill

陰影用 utilityshadow-card(一般卡片)、shadow-soft主按鈕、Hero、.auth-ticket)。內容 Hero 可用 ac-bulletin + ac-hero-gradient token全頁裝飾雲朵走 SceneDecor,不要另加會打架的強色 blob。

共用元件(優先復用)

新頁面必須從 src/components/ui.tsx 組裝,不要另寫一套按鈕樣式:

元件 用途
PageTitle 頁面標題 + 副標
Card 內容區塊
Field + Input / Textarea 表單
Button primary / ghost / danger / soft
Badge 標籤 pillbrand / sky / success / warning / danger / neutral
StatCard / QuickLinkCard 總覽統計與快捷入口
ErrorText / CopyableId 錯誤與可複製 ID

Button 必須渲染 {children};文案用中文動詞(例:「建立背景任務」「重新載入任務列表」),不要留空白小框。

RWD手機

  • < lg:隱藏左側欄;底部固定導覽最多 4 格(總覽 / 任務 / 排程 / 更多),不要把漢堡或 ⋯ 選單放在左上角。
  • 「更多」以底部 sheet 展開AI、模板、設定、會員、權限、主題切換、登出。
  • 主內容加 layout-main 底部 padding避開 tab bar + safe-area-inset-bottom
  • 寬表格包 overflow-x-auto + min-w-*,避免小螢幕擠爆版面。

版面與導覽

  • 已登入(桌面):Layout = hx-scene 背景 + SceneDecor + ac-app-header(品牌 + 角色 chip + ThemeToggle+ 左 ac-pocket-device + 右 auth-ticket 主內容 Outlet
  • 已登入(手機):同上頂欄;導覽走 MobileBottomNav(總覽/任務/排程/更多)。
  • 側欄 App 來源:src/lib/acAssets.tsnavApps;圖示 key 對應 AcIcon
  • Active 導覽:ac-app-tile--activebrand-soft 底 + brand 字色hoverbg-brand-soft text-brand
  • 未登入:AuthShell 置中 auth-ticket + 右上 ThemeToggleauth-welcome 內品牌,表單緊接說明文字。
  • 語氣:年輕、直接、短句;可帶「島民」「巡樓」等原創文案,避免企業八股與任天堂用語。

API 與狀態

  • JSON 一律走 api/client.tscode/message/data envelope需登入加 { auth: true }
  • AI 路由用 X-Member-Authorizationprovider token 用 Authorization(見後端 Auth matrix
  • Job 狀態中文與 badge 色:src/lib/jobStatus.tsjobStatusLabel / jobStatusBadgeClass),列表有進行中任務時可每 3 秒 refresh。
  • 不要在前端 parse JWTuid / tenant_idAuthContext 讀。

新增頁面流程

  1. App.tsx 掛路由(需登入的放在 Layout 底下,自動享有 hx-scene + 頂欄 + 主內容 auth-ticket)。
  2. 頁面內用 PageTitle(含 .ac-title-bar 小標)+ Card / ac-bulletin + ui.tsx 元件;色票只引用 semantic token。
  3. 若需新語意色,index.css--hx-*@theme,再改元件;不要頁面內硬編色碼。
  4. 新導覽項:改 acAssets.tsnavApps,並在 AcIcon 補 SVG path。
  5. 完成後執行 make web-build

島民頁面互動(可推廣 runtime

掛在 Layout 底下的新頁面自動支援島民操作,不需每頁手寫 executor。

模組入口:web/src/lib/islander/index.ts

職責
pageSnapshot 掃描 .ac-app-shell 內可互動元素,產生 hx-* ref
islanderActions 解析/剝除 islander-actions JSON 區塊
actionExecutor 執行 navigate/click/fill/select/scroll 等;可 registerIslanderActionHandler 擴充
islanderAgent 串流回覆 → 執行 action → 回傳結果 → 自動 follow-up
buildIslanderContext 組裝送給後端的頁面快照

零設定(預設):路由掛在 Layout 即可;島民讀 DOM + PageTitle / h1 辨識頁面。預設主動介紹這一頁;僅在使用者明確問頁面/操作時才附【可互動元素】(userWantsPageContext)。

可選增強(擇一):

  1. useIslanderPage({ title, purpose, hints, suggestions }) — 頁面內動態註冊說明
  2. registerIslanderPage(/^\/foo/, { title, ... }) — 在 siteGuide.ts 或模組 init 靜態註冊
  3. HTML 慣例:data-islander-label(元素名稱)、data-islander-kind(類型)、data-islander-ignore(排除)、data-islander-page-title(頁名)

Action 協定AI 回覆末尾):

[{ "type": "navigate", "path": "/settings" }, { "type": "click", "ref": "hx-3" }]

前端禁忌

  • 不要引入 MUI / Ant Design / Chakra 等大型 UI 庫。
  • 不要為單頁新增第三套配色、木質頂欄、或漸層彩虹按鈕。
  • 不要在登入/註冊頁加回獨立大牌 ac-title-bar 或咖啡色 header ribbon。
  • 不要讓 SSE / AI 直接吃 provider 原始 chunk後端已 normalize
  • 不要用 offset/limit 呼叫列表 APIpage / pageSize

驗證

完成變更後至少執行:

cd haixun-backend
go mod tidy
make fmt
go test ./...

有動到前端時另執行:

make web-build