6a49f25aef
SDD: docs/3-specs/jdd-dual-profile(draft → active,leo 2026-08-05 回「開工」)
範圍:總管指定的「防炸兩件 → Phase 0 → Phase 1」,Phase 2 以後未開工。
■ 防炸(排在所有 task 之前,因為它們炸的是既有的東西)
- check-no-instance-names.sh + instance-names.txt:框架範本不得混入實例專名
基線實測 22 行命中(非先前誤報的 20)→ 9 行無損泛化改寫、13 行檔級豁免記帳待 W3 搬走
拒絕假性清理(把專名換成模糊詞=資訊消失、分層問題還在)
- check-legacy-paths.sh:已發佈腳本引用的 35 條遠端路徑只增不移
舊實例跑的是舊腳本、路徑寫死;搬檔=整排 404 且不會有下一次更新來修它(1.16.0 前科)
■ Phase 0 地基
- template/manifest/{common,repo,orchestrator}.tsv:安裝清單單一真相源
修好 install/update 兩份硬編清單的既有漂移——install 從不裝 wiki-first-search /
subagent-wiki-guard / publish-lag-check / decisions-summary,但 update 會
⇒ 乾淨安裝反而拿不到 1.16/1.17/1.18 的招牌功能
- .claude/hooks/lib/role-lib.sh:scope×role 兩軸機械判定,零自陳
身分矩陣六組實測全通過,含「成員 repo × orchestrator」不存在的格子擋下
- .sdt-framework-dev:框架開發標記(官方沒有 --framework-dev 這個參數,實查非記憶)
■ Phase 1 雙 profile
- profiles/{repo,orchestrator}/CLAUDE.md 兩部憲法
- install.sh:--profile + 自動偵測+寫檔前確認、manifest 驅動、
CLAUDE.md 三段組裝(框架區/本地補充區界標+sha256)、.profile、.template-manifest、
settings.json 寫入 env.AGENT_ROLE 預設
- update.sh:漂移偵測(不覆蓋手改檔、另存 .new、白話清單)+ 基準快照隨更新前進
- template/CLAUDE.md 原路徑凍結留底(相容)
■ 順手修掉兩個舊 bug(都在本次要動的函式裡)
- add_if_missing 少了 mkdir -p ⇒ 新目錄的檔 curl 失敗但 VERSION 照升(2026-07 記「待回報」至今未修)
- 下載健全性只用 [ -s ]=非空即接受 ⇒ 404 頁面會無聲覆寫好檔
(SKILL.md 260→1 行的機制;同一支腳本的版本號那條路早就防了,檔案這條沒防)
■ 實測(非推論)
- G4 憲法分流:兩個乾淨環境各裝一次,orchestrator 版含 SDD 三件式關鍵字 0 次、
repo 版含上游指針 8 次;界標 4/4;sha 宣告與實算相符
- G7 CI 擋實例名:注入違規行 → fail 並指出 sdd-check.md:77,exit 1;還原後 exit 0
- 漂移偵測:手改兩支 hook → 正確報 2 支、手改內容保住、產 .new;
解掉後歸零;連跑三輪冪等
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
159 lines
7.4 KiB
YAML
159 lines
7.4 KiB
YAML
# tasks-project-sync — tasks.md ⇄ GitHub Project 單向投影
|
||
#
|
||
# 來源:issue #16;設計:system-dev/docs/3-specs/tasks-project-projection/design.md
|
||
#
|
||
# ⚠️ sdt-instance-name-ok-file — 檔級豁免(實例專名檢查)
|
||
# 理由:整支是 **L2 政策包產物**,不是 L1 框架內容——它綁定某一家的工作流引擎,
|
||
# 「換一家公司就不成立」。它現在還躺在框架裡只是歷史包袱。
|
||
# 帳記在這:**W3(政策包 plugin 化)時整組移出 template/**,屆時本豁免一併刪除。
|
||
# 在那之前不做假性清理(把引擎名改寫成模糊詞=資訊消失、分層問題還在)。
|
||
#
|
||
# ── 這份 workflow 的職責邊界(很重要,別搞混)──────────────────
|
||
# arcrun workflow 在 Cloudflare Workers / WASM 上「遠端」執行,沒有本地檔案系統、
|
||
# 沒有 git、沒有 shell。所以「讀 tasks.md / 跑 git diff / 把 <!-- gh:id --> 回寫 md」
|
||
# 這三件事 **不是、也不該是 workflow 的步驟**——它們由本地觸發端(CC / push 後本機腳本)
|
||
# 先做完,把「分類好的 task 增量」當 input 餵進來(acr run -i tasks_json=...)。
|
||
#
|
||
# 本地端(住 template,CC/shell 跑):讀 tasks.md → git diff → 分類四動作 → 回寫 id
|
||
# │ acr run tasks-project-sync -i ...(把增量餵進來)
|
||
# ▼
|
||
# 遠端 workflow(這份 yaml):foreach 增量 → switch 動作 → github API 投影
|
||
#
|
||
# → 因此本 workflow 只負責「拿到分類好的增量後,打 GitHub API 投影成 issue/Project」。
|
||
# 單向:只寫 GitHub,永不回改 tasks.md(回寫 id 是本地端的事,且只在新 task 做一次)。
|
||
#
|
||
# ── 輸入(由本地觸發端用 acr run -i 餵)──────────────────────────
|
||
# owner GitHub repo owner(例:your-github-account)
|
||
# repo GitHub repo 名
|
||
# project_id GitHub Projects v2 的 node id(投影目標,唯讀看板)
|
||
# tasks_json 本地分類好的增量陣列,每筆形如:
|
||
# { action: "create|close|edit|archive",
|
||
# gh: 42, # 已有 id 的帶上(create 無)
|
||
# title: "...", body: "...",
|
||
# subsystem: "wiki-architecture", # = SDD folder 名,當 label 分組
|
||
# assignee: "...", due: "..." }
|
||
#
|
||
# ── credential ─────────────────────────────────────────────────
|
||
# github_token(acr auth-recipe scaffold github → 填 credentials.yaml → acr creds push)
|
||
#
|
||
# ⚠️ 端到端(acr push 真部署 + acr run 真投影)尚未經 leo21c 驗證。
|
||
# 本檔為 code-done 骨架;真部署時 GitHub API 的欄位細節(Projects v2 GraphQL)
|
||
# 可能要按實測微調,屆時於 issue #16 回報。
|
||
|
||
name: tasks_project_sync
|
||
description: >
|
||
把 SDD tasks.md 的待辦增量單向投影成唯讀 GitHub Project(issue CRUD + 加進 Project)。
|
||
本地端先讀檔/git diff/分類/回寫 id,這份只負責拿增量打 GitHub API。md 當家、單向、不反向同步。
|
||
|
||
# ── flow(三元組:A >> 關係詞 >> B)─────────────────────────────
|
||
# 對每筆增量 → 依 action 路由到四種 GitHub 動作。
|
||
flow:
|
||
- "input >> 完成後 >> each_task"
|
||
- "each_task >> 對每個 task >> route_action"
|
||
# 四種動作(issue 定案):新增/關閉/編輯/封存
|
||
- "route_action >> 完成後 >> gh_create"
|
||
- "route_action >> 完成後 >> gh_close"
|
||
- "route_action >> 完成後 >> gh_edit"
|
||
- "route_action >> 完成後 >> gh_archive"
|
||
# 新建的 issue 投影進 Project(唯讀看板)
|
||
- "gh_create >> 完成後 >> add_to_project"
|
||
|
||
config:
|
||
# 逐筆迭代本地餵進來的分類增量
|
||
each_task:
|
||
component: foreach_control
|
||
iterator: task
|
||
|
||
# 依 action 欄位分流到四種 GitHub 動作
|
||
route_action:
|
||
component: switch
|
||
key: "{{task.action}}"
|
||
cases:
|
||
create: gh_create # 有文字、無 id → 建 issue(id 由本地端回寫 md)
|
||
close: gh_close # 有 id 且 [ ]→[x] → 關 issue
|
||
edit: gh_edit # 有 id 且 文字/負責人/日期改 → 編輯 issue
|
||
archive: gh_archive # id 在但整行不見 → 關閉/封存
|
||
|
||
# ── 動作 1:建 issue(REST POST /repos/:owner/:repo/issues)──
|
||
# 回傳的 issue number 由本地觸發端接住、回寫 <!-- gh:number --> 到那一行。
|
||
# ⚠️ 用 http_request(arcrun registry 沒有 github 零件,21 內建確認過);
|
||
# auth 走 credential:{{creds.github_token}}(acr creds push 上傳,不寫死 token)。
|
||
gh_create:
|
||
component: http_request
|
||
url: "https://api.github.com/repos/{{owner}}/{{repo}}/issues"
|
||
method: POST
|
||
headers:
|
||
Accept: "application/vnd.github+json"
|
||
Authorization: "Bearer {{creds.github_token}}"
|
||
User-Agent: "arcrun-tasks-project-sync"
|
||
body:
|
||
title: "{{task.title}}"
|
||
body: "{{task.body}}"
|
||
labels:
|
||
- "{{task.subsystem}}" # 子系統 label 分組(= SDD folder 名)
|
||
assignees:
|
||
- "{{task.assignee}}"
|
||
|
||
# ── 動作 2:關 issue(state=closed)──
|
||
gh_close:
|
||
component: http_request
|
||
url: "https://api.github.com/repos/{{owner}}/{{repo}}/issues/{{task.gh}}"
|
||
method: PATCH
|
||
headers:
|
||
Accept: "application/vnd.github+json"
|
||
Authorization: "Bearer {{creds.github_token}}"
|
||
User-Agent: "arcrun-tasks-project-sync"
|
||
body:
|
||
state: closed
|
||
|
||
# ── 動作 3:編輯 issue(標題/內文/負責人)──
|
||
gh_edit:
|
||
component: http_request
|
||
url: "https://api.github.com/repos/{{owner}}/{{repo}}/issues/{{task.gh}}"
|
||
method: PATCH
|
||
headers:
|
||
Accept: "application/vnd.github+json"
|
||
Authorization: "Bearer {{creds.github_token}}"
|
||
User-Agent: "arcrun-tasks-project-sync"
|
||
body:
|
||
title: "{{task.title}}"
|
||
body: "{{task.body}}"
|
||
assignees:
|
||
- "{{task.assignee}}"
|
||
|
||
# ── 動作 4:封存(行不見 = 關閉,標 not_planned 表示非完成而是移除)──
|
||
gh_archive:
|
||
component: http_request
|
||
url: "https://api.github.com/repos/{{owner}}/{{repo}}/issues/{{task.gh}}"
|
||
method: PATCH
|
||
headers:
|
||
Accept: "application/vnd.github+json"
|
||
Authorization: "Bearer {{creds.github_token}}"
|
||
User-Agent: "arcrun-tasks-project-sync"
|
||
body:
|
||
state: closed
|
||
state_reason: not_planned
|
||
|
||
# ── 投影進 GitHub Projects v2(GraphQL)──
|
||
# Projects v2 只有 GraphQL,REST 沒有。用 http_request 打 /graphql。
|
||
# ⚠️ 待 leo21c 端到端驗:addProjectV2ItemById 需要 content node id(issue 的 GraphQL id,
|
||
# 非 issue number),實測時可能要多一步「先查 issue node id」。屆時於 #16 回報。
|
||
add_to_project:
|
||
component: http_request
|
||
url: "https://api.github.com/graphql"
|
||
method: POST
|
||
headers:
|
||
Accept: "application/vnd.github+json"
|
||
Authorization: "Bearer {{creds.github_token}}"
|
||
User-Agent: "arcrun-tasks-project-sync"
|
||
body:
|
||
query: >
|
||
mutation($project: ID!, $content: ID!) {
|
||
addProjectV2ItemById(input: {projectId: $project, contentId: $content}) {
|
||
item { id }
|
||
}
|
||
}
|
||
variables:
|
||
project: "{{project_id}}"
|
||
content: "{{gh_create.node_id}}"
|