@namewta/speculo 1.0.19 → 1.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -7
- package/THIRD-PARTY-NOTICES.md +33 -0
- package/dist/src/agent-files.js +2 -2
- package/dist/src/agent-files.js.map +1 -1
- package/dist/src/agent-skills.d.ts +81 -0
- package/dist/src/agent-skills.js +530 -0
- package/dist/src/agent-skills.js.map +1 -0
- package/dist/src/cli.js +11 -3
- package/dist/src/cli.js.map +1 -1
- package/dist/src/config.js +5 -3
- package/dist/src/config.js.map +1 -1
- package/dist/src/doctor.js +7 -2
- package/dist/src/doctor.js.map +1 -1
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.js +23 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/manifest.d.ts +9 -0
- package/dist/src/manifest.js +45 -7
- package/dist/src/manifest.js.map +1 -1
- package/dist/src/refresh.js +40 -0
- package/dist/src/refresh.js.map +1 -1
- package/dist/src/structured.js +15 -4
- package/dist/src/structured.js.map +1 -1
- package/dist/src/transaction.d.ts +24 -3
- package/dist/src/transaction.js +191 -12
- package/dist/src/transaction.js.map +1 -1
- package/package.json +7 -4
- package/scripts/validate-skills.mjs +56 -0
- package/template/AGENTS.md +1 -1
- package/template/CLAUDE.md +1 -1
- package/template/canonical/canonical-specdev-goal-plan.md +156 -28
- package/template/canonical/canonical-specdev-grill-with-docs.md +153 -31
- package/template/canonical/canonical-specdev-spec.md +153 -27
- package/template/canonical/canonical-specdev-tickets.md +153 -27
- package/template/canonical/canonical-specdev-wayfinder.md +142 -27
- package/template/commands/retro.md +11 -9
- package/template/skills/archive-and-consolidate/SKILL.md +163 -7
- package/template/skills/archive-and-consolidate/references/cleanup-rules.md +1 -1
- package/template/skills/docs-sync/references/agents/claude-redirect.md +8 -8
- package/template/skills/docs-sync/references/agents/content-contract.md +1 -1
- package/template/skills/docs-sync/references/agents/manifest-discovery.md +7 -7
- package/template/skills/docs-sync/references/agents/role-classification.md +4 -4
- package/template/skills/docs-sync/references/agents-contract.md +1 -1
- package/template/skills/engineering-standards-builder/SKILL.md +149 -9
- package/template/skills/engineering-standards-builder/agents/openai.yaml +2 -0
- package/template/skills/engineering-standards-builder/manifest.txt +2 -1
- package/template/skills/engineering-standards-builder/references/rules/09-mechanical-guardrails.md +9 -0
- package/template/skills/engineering-standards-builder/scripts/validate-builder.mjs +4 -7
- package/template/skills/git-history-squash/SKILL.md +87 -11
- package/template/skills/git-history-squash/agents/openai.yaml +2 -0
- package/template/skills/optimize-codex-config/SKILL.md +71 -11
- package/template/skills/{speculo-retro/references/entry-procedure.md → retrospective/SKILL.md} +12 -8
- package/template/skills/{speculo-retro → retrospective}/references/friction-taxonomy.md +22 -13
- package/template/skills/{speculo-retro → retrospective}/references/issue-drafting-sop.md +10 -20
- package/template/skills/source-code-zip/SKILL.md +44 -8
- package/template/skills/source-code-zip/references/{entry-procedure.md → cli-reference.md} +0 -43
- package/template/skills/upstream-fork-sync/SKILL.md +71 -8
- package/template/skills/{writing-great-skills → writing-for-agents}/GLOSSARY.md +1 -1
- package/template/skills/writing-for-agents/SKILL.md +22 -0
- package/template/skills/writing-for-agents/agents/openai.yaml +2 -0
- package/template/skills/{writing-great-skills/references/entry-procedure.md → writing-for-agents/references/authoring-principles.md} +2 -9
- package/template/workflows/ops/common/tests/test_ops.mjs +5 -8
- package/template/workflows/ops/common/tests/test_ops_bootstrap.mjs +71 -14
- package/template/workflows/ops/common/tests/test_ops_workspace.mjs +17 -8
- package/template/workflows/ops/common/tools/opslib/bootstrap.mjs +4 -4
- package/template/workflows/ops/common/tools/opslib/transport.mjs +3 -3
- package/template/workflows/specdev/A-archive-and-consolidate/consolidation-interview.md +3 -3
- package/template/workflows/specdev/C-code-review/C-code-review.md +1 -1
- package/template/workflows/specdev/D-diagnose-bugs/D-diagnose-bugs.md +6 -0
- package/template/workflows/specdev/G-grill-with-docs/references/interview-procedure.md +6 -6
- package/template/workflows/specdev/I-implement/references/implementation-procedure.md +1 -1
- package/template/workflows/specdev/I-implement/tdd-rules.md +4 -0
- package/template/workflows/specdev/I-init-setup/config-template.json +5 -1
- package/template/workflows/specdev/INDEX.md +2 -2
- package/template/workflows/specdev/P-goal-plan/planning-modes.md +1 -1
- package/template/workflows/specdev/P-prototype/P-prototype.md +7 -3
- package/template/workflows/specdev/P-prototype/logic-protocol.md +11 -0
- package/template/workflows/specdev/P-prototype/logic-template.md +27 -0
- package/template/workflows/specdev/P-prototype/project-preview-protocol.md +9 -0
- package/template/workflows/specdev/R-retro/R-retro.md +26 -0
- package/template/workflows/specdev/R-retro/retro-template.md +27 -0
- package/template/workflows/specdev/R-review-architecture/references/selected-design.md +1 -1
- package/template/workflows/specdev/README.md +13 -2
- package/template/workflows/specdev/S-spec/S-spec.md +4 -0
- package/template/workflows/specdev/T-tickets/T-tickets.md +4 -0
- package/template/workflows/specdev/T-triage/T-triage.md +31 -25
- package/template/workflows/specdev/T-triage/capture-protocol.md +1 -1
- package/template/workflows/specdev/T-triage/ci-security-protocol.md +11 -0
- package/template/workflows/specdev/T-triage/intake-protocol.md +10 -4
- package/template/workflows/specdev/T-triage/pr-delivery-protocol.md +13 -0
- package/template/workflows/specdev/T-triage/pr-record-template.md +39 -0
- package/template/workflows/specdev/T-triage/publish-protocol.md +1 -1
- package/template/workflows/specdev/T-triage/queue-protocol.md +7 -0
- package/template/workflows/specdev/T-triage/reconcile-protocol.md +1 -1
- package/template/workflows/specdev/T-triage/references/issue-pr-policy.md +23 -0
- package/template/workflows/specdev/T-triage/references/release-authentication.md +9 -0
- package/template/workflows/specdev/T-triage/references/release-notes-injection.md +21 -0
- package/template/workflows/specdev/T-triage/references/workflow-yaml-reference.md +37 -0
- package/template/workflows/specdev/T-triage/release-preflight.md +13 -0
- package/template/workflows/specdev/T-triage/release-protocol.md +12 -0
- package/template/workflows/specdev/T-triage/release-recovery.md +18 -0
- package/template/workflows/specdev/T-triage/remote-operations.md +25 -0
- package/template/workflows/specdev/T-triage/scripts/github-transport.mjs +208 -0
- package/template/workflows/specdev/T-triage/source-template.md +7 -0
- package/template/workflows/specdev/T-triage/triage-run-template.md +34 -0
- package/template/workflows/specdev/T-triage/triage-template.md +4 -1
- package/template/workflows/specdev/common/rules/artifact-contract.md +7 -0
- package/template/workflows/specdev/common/rules/change-completion.md +6 -0
- package/template/workflows/specdev/common/rules/workflow-routing.md +6 -0
- package/template/workflows/specdev/common/rules/workflow-state-and-lifecycle.md +1 -1
- package/template/workflows/specdev/common/schemas/config.schema.json +137 -26
- package/template/workflows/specdev/common/schemas/logic-prototype.schema.json +62 -0
- package/template/workflows/specdev/common/schemas/pull-request.schema.json +90 -0
- package/template/workflows/specdev/common/schemas/retrospective.schema.json +58 -0
- package/template/workflows/specdev/common/schemas/triage-run.schema.json +149 -0
- package/template/workflows/specdev/common/schemas/triage.schema.json +117 -15
- package/template/workflows/specdev/common/tools/delivery-artifacts.mjs +95 -0
- package/template/workflows/specdev/common/tools/validate-specdev.mjs +60 -19
- package/template/workflows/specdev/runtime-contract.json +4 -2
- package/template/skills/archive-and-consolidate/references/entry-procedure.md +0 -178
- package/template/skills/docs-sync/references/entry-procedure.md +0 -3
- package/template/skills/engineering-standards-builder/references/entry-procedure.md +0 -154
- package/template/skills/git-history-squash/references/entry-procedure.md +0 -94
- package/template/skills/github-npm-ops/SKILL.md +0 -19
- package/template/skills/github-npm-ops/references/ci-and-security-ops.md +0 -178
- package/template/skills/github-npm-ops/references/entry-procedure.md +0 -25
- package/template/skills/github-npm-ops/references/failure-recovery.md +0 -123
- package/template/skills/github-npm-ops/references/issue-pr-triage.md +0 -227
- package/template/skills/github-npm-ops/references/issue-transport.md +0 -26
- package/template/skills/github-npm-ops/references/package-json-checklist.md +0 -171
- package/template/skills/github-npm-ops/references/preflight-checklist.md +0 -39
- package/template/skills/github-npm-ops/references/publish-detection.md +0 -68
- package/template/skills/github-npm-ops/references/release-notes-injection.md +0 -238
- package/template/skills/github-npm-ops/references/release-pipeline.md +0 -108
- package/template/skills/github-npm-ops/references/setup-npm-token.md +0 -63
- package/template/skills/github-npm-ops/references/troubleshooting-playbook.md +0 -293
- package/template/skills/github-npm-ops/references/version-bump-flow.md +0 -217
- package/template/skills/github-npm-ops/references/workflow-yaml-reference.md +0 -268
- package/template/skills/github-npm-ops/scripts/issue-transport.mjs +0 -227
- package/template/skills/optimize-codex-config/references/entry-procedure.md +0 -78
- package/template/skills/speculo-retro/SKILL.md +0 -19
- package/template/skills/upstream-fork-sync/references/entry-procedure.md +0 -78
- package/template/skills/writing-great-skills/SKILL.md +0 -23
- /package/template/skills/{writing-great-skills → writing-for-agents}/references/document-contract.md +0 -0
- /package/template/workflows/specdev/{G-grill-with-docs → common/rules}/adr-format.md +0 -0
- /package/template/workflows/specdev/{G-grill-with-docs → common/rules}/context-format.md +0 -0
- /package/template/workflows/specdev/{I-implement → common/rules}/design-it-twice.md +0 -0
- /package/template/workflows/specdev/{G-grill-with-docs/domain-modeling-rules.md → common/rules/domain-modeling.md} +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# 版本说明与 GitHub Release 正文
|
|
2
|
+
|
|
3
|
+
CHANGELOG 中已确认的本版本段落是正文来源;保留 Unreleased。项目使用其他权威发布说明时记录来源。自动生成 notes 可作为补充,不冒充实际变更与风险说明。
|
|
4
|
+
|
|
5
|
+
## 提取与验证
|
|
6
|
+
|
|
7
|
+
按项目实际标题格式与语言读取精确版本段落,边界到下一同级版本标题;不要用只适配某种标题的 awk 段落猜测。提取后验证非空、版本一致,包含实际变化、迁移/兼容和风险说明。必要内容缺失则停止发布准备,不用兜底成功文案替代。
|
|
8
|
+
|
|
9
|
+
多语言项目遵守已确定的主语言或合并策略,不擅自丢弃一种语言。生成正文的临时文件仅用于 --notes-file,不成为新的永久知识源。
|
|
10
|
+
|
|
11
|
+
## 交付
|
|
12
|
+
|
|
13
|
+
使用 gh release create/edit 的 --notes-file 或仓库既有 action 的 body_path,按原计划设置 draft/prerelease/latest。调用方必须持有对应远程写入授权。生成器和 API 可能追加 compare 链接,读回时核对关键版本内容而非只看正文非空。
|
|
14
|
+
|
|
15
|
+
## 已发布正文补全
|
|
16
|
+
|
|
17
|
+
先固定 repo/tag/SHA 与当前正文,比较人工变更。只有明确要求补全的准确版本才更新,记录授权、前后摘要和读回结果。已有 npm 包不重发,tag 不移动。批量修复需要明确版本集合,逐项记账,失败停止后续写入。
|
|
18
|
+
|
|
19
|
+
## 归属
|
|
20
|
+
|
|
21
|
+
T 的发布使用原 TRI 回执;其他调用方直接复用时写自己的报告。docs-sync state 与知识仍由 docs-sync/A 原 owner 负责。
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# 发布流水线参考
|
|
2
|
+
|
|
3
|
+
这是按项目改写的示例,不是必须安装的 release.yml。实际名称、触发器、Node/包管理器版本、包集合、registry、渠道和质量闸来自发布预检。示例动作版本在使用时核对官方文档,沿用项目的版本固定策略。
|
|
4
|
+
|
|
5
|
+
## 阶段
|
|
6
|
+
|
|
7
|
+
1. checkout 固定 tag/commit,核对 tag 对应的 release SHA 与包版本。
|
|
8
|
+
2. 安装项目声明的运行时与包管理器,用冻结 lockfile 安装依赖。
|
|
9
|
+
3. 执行项目 test/typecheck/lint/build 及 CLI bin/pack 检查。
|
|
10
|
+
4. 提取已确认的本版本 release notes;必需正文缺失即失败。
|
|
11
|
+
5. 对每个 required 包按明确 registry/access/dist-tag 发布;OIDC/Token 由认证合同选定。
|
|
12
|
+
6. 创建或补全 GitHub Release,prerelease/latest 设置与原发布计划一致。
|
|
13
|
+
7. 返回 run ID、SHA 与发布回执,由 T 重读验证。
|
|
14
|
+
|
|
15
|
+
## GitHub Actions 片段
|
|
16
|
+
|
|
17
|
+
```yaml
|
|
18
|
+
permissions:
|
|
19
|
+
contents: write
|
|
20
|
+
id-token: write # 采用 OIDC/provenance 且确有需要时
|
|
21
|
+
# checkout/setup-node/包管理器的 uses 与版本按项目政策固定
|
|
22
|
+
# 以下变量由已确认的发布计划提供
|
|
23
|
+
steps:
|
|
24
|
+
- run: pnpm install --frozen-lockfile
|
|
25
|
+
- run: pnpm check
|
|
26
|
+
- run: npm pack --dry-run
|
|
27
|
+
- run: npm publish --registry "$REGISTRY" --tag "$DIST_TAG" --access "$PACKAGE_ACCESS"
|
|
28
|
+
- run: gh release create "$TAG" --verify-tag --title "$RELEASE_TITLE" --notes-file release-notes.md
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
此片段省略了项目特定的 job、认证与变量注入,不能原样当成可执行完整 workflow。发布顺序不是回滚保证;npm 成功后后续失败按恢复协议补缺项。多包、可复用 workflow、自建 runner 和人工发布需在预检明确其真实行为。
|
|
32
|
+
|
|
33
|
+
最低权限按实际动作配置,纯 npm 不因示例而增加 packages:write。id-token:write 本身不证明 npm 已配置 trust。远程 workflow 更新仍经正常 I/C 流程,不能由 release-preflight 自动落文件。
|
|
34
|
+
|
|
35
|
+
## 检查发布包
|
|
36
|
+
|
|
37
|
+
核对 name/version、repository、license、private/publishConfig、files/exports/main/types、bin/shebang/执行权限、engines/packageManager 和依赖分类。以 npm pack --dry-run 的实际清单检查缺失入口、秘密文件、测试/缓存误入;CLI 路径从本项目 manifest 获取,不硬编码示例路径。新包的 404 还需区分权限/registry,不直接保证名字可用。
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# 发布预检
|
|
2
|
+
|
|
3
|
+
读取 <Path>{roots.workflows}/specdev/T-triage/triage-run-template.md</Path>,创建或恢复 T 拥有的独立操作记录。预检只读远程与项目事实,不自动修复环境。记录:
|
|
4
|
+
|
|
5
|
+
1. repo、发布分支/渠道、工作区状态、明确的目标 commit SHA、版本、tag;以项目声明为准,无法确定时停止。
|
|
6
|
+
2. package.json、workspace/lockfile、engines 完整版本范围、packageManager、包集合、registry、access、dist-tag;不能把所有版本限制解释为大于等于。
|
|
7
|
+
3. 实际 workflow 路径、trigger、复用 workflow 和调用脚本。npm_target 必须为 required/not-required/unknown;grep 仅作定位,包装脚本或表达式不明时为 unknown 并阻塞 release。
|
|
8
|
+
4. 每个包的目标版本与 registry 是否已有版本,tag 是否存在且指向预期 SHA。冲突先恢复/更换版本,不建议删除远程 tag。
|
|
9
|
+
5. gh 身份和所需的具体权限,发布认证按 <Path>{roots.workflows}/specdev/T-triage/references/release-authentication.md</Path>;不要求无关管理员权限,不保存凭据。
|
|
10
|
+
6. 项目声明的 test/typecheck/lint/build/bin/pack 等闸门与证据、CHANGELOG 版本内容、预期 Release 正文。缺失基础设施交 I;按需参考 <Path>{roots.workflows}/specdev/T-triage/references/workflow-yaml-reference.md</Path>,不在预检中安装。
|
|
11
|
+
7. 需要文档同步时,由 <Path>{roots.commands}/docs-sync.md</Path> 处理其 state、bootstrap 和范围;T 不改 sidecar。
|
|
12
|
+
|
|
13
|
+
完成条件:记录中的 targets 数组及 Targets 正文列出每个包、registry、版本、dist-tag 与发布判定,Evidence 引用固定 SHA 的检查;unknown/冲突保留 blocked。预检通过并不授权 commit/push/tag/发布。
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# npm / GitHub Release 发布
|
|
2
|
+
|
|
3
|
+
先执行 <Path>{roots.workflows}/specdev/T-triage/release-preflight.md</Path>;读取已有 TRI 记录,模式变为 release 并保留预检证据。每阶段原子写入回执并重读。T 不写 docs-sync state,不更改 change 的完成事实。
|
|
4
|
+
|
|
5
|
+
1. 明确发布范围,展示普通提交、docs-sync、版本修改、release commit、分支/tag push、workflow 和发布目标的准确计划。只执行已获授权动作,沿用仍有效授权。
|
|
6
|
+
2. 如有待提交改动,按项目规则显式选路径,不用 stash 隐藏内容。质量闸通过后才能提交。调用 docs-sync 时遵守其范围和提交协议。
|
|
7
|
+
3. 保留 Unreleased,按项目格式迁移本版本 CHANGELOG;同步 package/workspace/lockfile 的必要版本。将版本与发布说明固定于同一 release commit,直接记录 SHA,不从模糊 commit message 推断。
|
|
8
|
+
4. 按批准的仓库策略推送分支并创建/推送精确指向该 SHA 的 tag。不自动 merge、不移动已有 tag。触发的 workflow 必须按准确 SHA、event、workflow 路径和 run ID 定位,不能只看最新一条 run。
|
|
9
|
+
5. 监控该 run;失败进入 <Path>{roots.workflows}/specdev/T-triage/release-recovery.md</Path>。成功后校验 workflow 与 SHA、GitHub Release tag/正文/发布状态,以及每个 required npm 包的明确 package@version 和目标 dist-tag。prerelease/非 latest 按原计划验证。
|
|
10
|
+
6. Release 正文来自已确认版本说明,按需读取 <Path>{roots.workflows}/specdev/T-triage/references/release-notes-injection.md</Path>。需要补记 docs-sync 输入节点时再次调用 docs-sync,由它维护游标;tag 保持不变。
|
|
11
|
+
|
|
12
|
+
记录最终 SHA/tag/run ID、每个目标读回结果、docs-sync 结果或不适用原因。任一 required 目标未知或失败不得标 completed,保留已发布事实和后续恢复条件。全部通过才将 TRI 标 completed,运行 --triage-run 校验并返回报告。
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# 恢复原操作
|
|
2
|
+
|
|
3
|
+
输入必须是已有 TRI/PR/capture/publish/来源回写记录的准确路径与原目标。恢复原记录,不另建成功记录替代失败;检查 owner、锁、漂移和仍有效的授权。普通远程动作按 <Path>{roots.workflows}/specdev/T-triage/remote-operations.md</Path> 恢复。
|
|
4
|
+
|
|
5
|
+
发布先重新读取准确 tag/SHA、workflow run、Release、registry 中的 package@version、dist-tag。npm 状态为未知时先解决读取/权限问题,不以未查到日志证明未上传。
|
|
6
|
+
|
|
7
|
+
| 实际状态 | 恢复 |
|
|
8
|
+
|---|---|
|
|
9
|
+
| 尚未发布,原 SHA 未变,失败原因为暂态且已排除 | 展示并执行已授权的指定 run/job 重跑;重跑后完整验证 |
|
|
10
|
+
| 尚未发布,但需要代码/配置变更 | 交 I 修复,形成新的 commit/发布计划;不默认覆盖远程 tag |
|
|
11
|
+
| npm 已发布,GitHub Release/正文失败 | 只补缺失的 GitHub 动作,不重新发布 npm;记录读回 |
|
|
12
|
+
| npm 已发布,包内容错误 | 新版本修复,保留旧版本与 tag 的事实;不尝试 unpublish 后覆盖 |
|
|
13
|
+
| Release 已有人工修改或 SHA 漂移 | 停止受影响更新,比较差异并重新确定计划 |
|
|
14
|
+
| 多包部分成功 | 逐包记账,仅继续确认未发布且仍匹配原计划的目标 |
|
|
15
|
+
|
|
16
|
+
相同根因重试必须记录新证据或与上次的差异;没有差异先停止。不得要求提供 Token 片段作为证明。
|
|
17
|
+
|
|
18
|
+
错误定位:认证失败核对权限/有效期/OIDC 配置;provenance 核对 runner、repository 和 id-token;E422 核对 tag/Release 已存在与正文;EUSAGE 核对实际 CLI 版本和参数。不要通过默认删 tag、force-push 或删除远程对象处理这些错误。恢复后的 completed 仍需原发布协议的所有目标校验。
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# GitHub 远程操作合同
|
|
2
|
+
|
|
3
|
+
本协议及 <Path>{roots.workflows}/specdev/T-triage/scripts/github-transport.mjs</Path> 由 T 唯一维护。C、command 或其他调用方可直接复用:不激活 T、不创建 change、不改 current_work;原调用方拥有报告、授权与回执。
|
|
4
|
+
|
|
5
|
+
## 输入与操作
|
|
6
|
+
|
|
7
|
+
显式传入 repo、目标编号或分支、正文文件和操作。读取失败、分页不完整、对象类型不符、SHA/正文漂移时停止受影响操作。
|
|
8
|
+
|
|
9
|
+
- issue-read / pr-read:完整可见内容;PR 包含评论、reviews、review comments、文件与 diff、不可变 base/head SHA、读取时间和完整性证据。
|
|
10
|
+
- issue-search:候选检索;limit 是检索范围,不代表已穷尽仓库。
|
|
11
|
+
- issue-create:title、body-file、labels,可选 marker;调用方先去重。
|
|
12
|
+
- issue-comment-close:comment-file、marker、reason;按 marker 幂等评论,执行后确认关闭。
|
|
13
|
+
- pr-create:base、head(同仓分支或 owner:branch)、base-sha、head-sha、title、body-file、marker;默认创建 draft。
|
|
14
|
+
- pr-update:number、base-sha、head-sha、expected-body-sha256、title、body-file、marker。
|
|
15
|
+
- pr-ready:number、base-sha、head-sha、expected-body-sha256;另需本次可审查授权。
|
|
16
|
+
|
|
17
|
+
运行脚本 --help 查看完整参数。所有参数使用 argv,不拼 shell。操作返回 JSON,失败退出非零;脚本不写任何 workflow 或 command 状态。
|
|
18
|
+
|
|
19
|
+
## 写入与恢复
|
|
20
|
+
|
|
21
|
+
默认 dry-run,外部写入为零。调用方在写前展示准确目标、内容与动作并取得明确授权后才传 --apply。已授权同一动作不重复索取授权;目标/内容发生实质变化则重新确认范围。PR、push、merge、release 各自独立。
|
|
22
|
+
|
|
23
|
+
对外正文标明 AI 协助。持久工件不保存凭据、Cookie、认证输出、机器绝对路径或未脱敏日志。临时正文文件由调用方管理。
|
|
24
|
+
|
|
25
|
+
执行前重读目标和预期 SHA/正文摘要;执行后重读验证。超时不表示未写入,先查询稳定 marker/目标再恢复,不能盲目重建。重复 marker、多候选、并发漂移或读回不符时返回失败,由调用方保存已完成步骤和恢复条件。本地开发事实不因远程失败回滚。
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import { resolve } from "node:path";
|
|
7
|
+
import { pathToFileURL } from "node:url";
|
|
8
|
+
|
|
9
|
+
const exec = promisify(execFile);
|
|
10
|
+
export const sha256 = value => createHash("sha256").update(value).digest("hex");
|
|
11
|
+
const must = (value, message) => { if (!value) throw new Error(message); };
|
|
12
|
+
const commentFields = value => ({ ...value, author: value.user ?? value.author, createdAt: value.created_at ?? value.createdAt, updatedAt: value.updated_at ?? value.updatedAt, url: value.html_url ?? value.url });
|
|
13
|
+
function required(o, keys) { for (const k of keys) must(typeof o[k] === "string" && o[k].trim(), "missing --" + k.replaceAll("_", "-")); }
|
|
14
|
+
function validateTarget(o) {
|
|
15
|
+
must(/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(o.repo ?? ""), "invalid repo");
|
|
16
|
+
if (o.number !== undefined) must(/^[1-9]\d*$/.test(String(o.number)), "invalid number");
|
|
17
|
+
if (o.marker !== undefined) must(/^[a-zA-Z0-9:_./-]+$/.test(o.marker), "invalid marker");
|
|
18
|
+
}
|
|
19
|
+
export function createTransport(run = async args => {
|
|
20
|
+
try { return (await exec("gh", args, { encoding: "utf8", maxBuffer: 64 * 1024 * 1024 })).stdout.trim(); }
|
|
21
|
+
catch { throw new Error("GitHub operation failed; remote outcome may be unknown. Re-read before retrying (raw credential-bearing stderr omitted)."); }
|
|
22
|
+
}, readText = path => readFile(path, "utf8")) {
|
|
23
|
+
const json = async args => JSON.parse(await run(args));
|
|
24
|
+
const api = path => json(["api", path]);
|
|
25
|
+
const pages = async path => {
|
|
26
|
+
const value = await json(["api", path + (path.includes("?") ? "&" : "?") + "per_page=100", "--paginate", "--slurp"]);
|
|
27
|
+
must(Array.isArray(value) && value.every(Array.isArray), "incomplete or invalid pagination");
|
|
28
|
+
return value.flat();
|
|
29
|
+
};
|
|
30
|
+
async function readIssue(repo, number) {
|
|
31
|
+
const item = await api("repos/" + repo + "/issues/" + number);
|
|
32
|
+
must(!item.pull_request, "target is a pull request; use pr-read");
|
|
33
|
+
const comments = await pages("repos/" + repo + "/issues/" + number + "/comments");
|
|
34
|
+
must(comments.length === item.comments, "issue comments changed or pagination incomplete");
|
|
35
|
+
const after = await api("repos/" + repo + "/issues/" + number);
|
|
36
|
+
must(after.updated_at === item.updated_at && after.body === item.body && after.comments === item.comments && after.state === item.state, "issue drift during read");
|
|
37
|
+
return { ...item, author: item.user, state: item.state.toUpperCase(), url: item.html_url, comments: comments.map(commentFields),
|
|
38
|
+
createdAt: item.created_at, updatedAt: item.updated_at,
|
|
39
|
+
pagination_complete: true, fetched_at: new Date().toISOString() };
|
|
40
|
+
}
|
|
41
|
+
async function readPull(repo, number) {
|
|
42
|
+
const root = "repos/" + repo + "/pulls/" + number;
|
|
43
|
+
const before = await api(root);
|
|
44
|
+
const [comments, reviews, review_comments, files, diff] = await Promise.all([
|
|
45
|
+
pages("repos/" + repo + "/issues/" + number + "/comments"), pages(root + "/reviews"),
|
|
46
|
+
pages(root + "/comments"), pages(root + "/files"),
|
|
47
|
+
run(["api", root, "-H", "Accept: application/vnd.github.diff"]),
|
|
48
|
+
]);
|
|
49
|
+
const after = await api(root);
|
|
50
|
+
must(before.base.sha === after.base.sha && before.head.sha === after.head.sha && before.updated_at === after.updated_at &&
|
|
51
|
+
before.body === after.body && before.comments === after.comments && before.review_comments === after.review_comments, "PR drift during read");
|
|
52
|
+
must(files.length === after.changed_files && comments.length === after.comments && review_comments.length === after.review_comments,
|
|
53
|
+
"PR pagination incomplete");
|
|
54
|
+
must(typeof diff === "string" && (after.changed_files === 0 || diff.includes("diff --git")), "missing PR diff");
|
|
55
|
+
return { number: after.number, title: after.title, body: after.body ?? "", state: after.merged_at ? "MERGED" : after.state.toUpperCase(),
|
|
56
|
+
url: after.html_url, author: after.user, labels: after.labels, createdAt: after.created_at, updatedAt: after.updated_at,
|
|
57
|
+
baseRefName: after.base.ref, headRefName: after.head.ref, baseRefOid: after.base.sha, headRefOid: after.head.sha,
|
|
58
|
+
isDraft: after.draft, comments: comments.map(commentFields), reviews: reviews.map(commentFields), review_comments: review_comments.map(commentFields),
|
|
59
|
+
files: files.map(file => ({ ...file, path: file.filename ?? file.path })), diff, pagination_complete: true,
|
|
60
|
+
body_sha256: sha256(after.body ?? ""), fetched_at: new Date().toISOString() };
|
|
61
|
+
}
|
|
62
|
+
function fixed(o, p) {
|
|
63
|
+
for (const k of ["base_sha", "head_sha"]) must(/^[a-f0-9]{40,64}$/.test(o[k] ?? ""), "invalid " + k);
|
|
64
|
+
must(p.baseRefOid === o.base_sha && p.headRefOid === o.head_sha, "PR SHA drift");
|
|
65
|
+
if (o.expected_body_sha256 !== undefined) must(p.body_sha256 === o.expected_body_sha256, "PR body drift");
|
|
66
|
+
}
|
|
67
|
+
async function candidate(o) {
|
|
68
|
+
const qualifiedHead = o.head.includes(":") ? o.head : o.repo.split("/")[0] + ":" + o.head;
|
|
69
|
+
const found = await pages("repos/" + o.repo + "/pulls?state=all&head=" + encodeURIComponent(qualifiedHead) + "&base=" + encodeURIComponent(o.base));
|
|
70
|
+
const matches = found.filter(p => String(p.body ?? "").includes("<!-- " + o.marker + " -->"));
|
|
71
|
+
must(matches.length < 2, "multiple PRs with marker");
|
|
72
|
+
if (matches.length) return readPull(o.repo, String(matches[0].number));
|
|
73
|
+
must(found.every(p => p.state !== "open"), "existing open PR for branches needs explicit update");
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
async function createPr(o, plan) {
|
|
77
|
+
required(o, ["base", "head", "base_sha", "head_sha", "title", "body_file", "marker"]);
|
|
78
|
+
const raw = await readText(o.body_file);
|
|
79
|
+
const marker = "<!-- " + o.marker + " -->";
|
|
80
|
+
const body = raw.includes(marker) ? raw : raw.trimEnd() + "\n\n" + marker;
|
|
81
|
+
const existing = await candidate(o);
|
|
82
|
+
if (existing) {
|
|
83
|
+
fixed(o, existing);
|
|
84
|
+
must(existing.body === body && existing.title === o.title, "existing PR content differs; use explicit update");
|
|
85
|
+
return { ...plan, status: "already-present", ...existing };
|
|
86
|
+
}
|
|
87
|
+
const base = await api("repos/" + o.repo + "/commits/" + encodeURIComponent(o.base));
|
|
88
|
+
const colon = o.head.indexOf(":");
|
|
89
|
+
if (colon >= 0) required(o, ["head_repo"]);
|
|
90
|
+
const headRepo = colon >= 0 ? o.head_repo : o.repo;
|
|
91
|
+
must(/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(headRepo), "invalid head-repo");
|
|
92
|
+
if (colon >= 0) must(headRepo.split("/")[0] === o.head.slice(0, colon), "head owner disagrees with head-repo");
|
|
93
|
+
const head = await api("repos/" + headRepo + "/commits/" + encodeURIComponent(colon >= 0 ? o.head.slice(colon + 1) : o.head));
|
|
94
|
+
fixed({ ...o, expected_body_sha256: undefined }, { baseRefOid: base.sha, headRefOid: head.sha });
|
|
95
|
+
if (!o.apply) return { ...plan, body_sha256: sha256(body), draft: true };
|
|
96
|
+
let failure;
|
|
97
|
+
try { await run(["pr", "create", "--repo", o.repo, "--base", o.base, "--head", o.head, "--title", o.title, "--body", body, "--draft"]); }
|
|
98
|
+
catch (e) { failure = e; }
|
|
99
|
+
const after = await candidate(o);
|
|
100
|
+
if (!after) throw failure ?? new Error("PR create result unknown; query marker before retry");
|
|
101
|
+
fixed(o, after);
|
|
102
|
+
must(after.body === body && after.title === o.title && after.isDraft, "PR creation readback mismatch");
|
|
103
|
+
return { ...plan, status: "created", ...after };
|
|
104
|
+
}
|
|
105
|
+
return async function transport(operation, options) {
|
|
106
|
+
const o = { labels: [], ...options }; validateTarget(o);
|
|
107
|
+
const plan = { operation, provider: "github", repo: o.repo, mode: o.apply ? "apply" : "dry-run" };
|
|
108
|
+
if (operation === "issue-read") { required(o, ["number"]); return { ...plan, kind: "issue", ...await readIssue(o.repo, o.number) }; }
|
|
109
|
+
if (operation === "pr-read") { required(o, ["number"]); return { ...plan, kind: "pull-request", ...await readPull(o.repo, o.number) }; }
|
|
110
|
+
if (operation === "issue-search") {
|
|
111
|
+
required(o, ["query"]);
|
|
112
|
+
const issues = await json(["issue", "list", "--repo", o.repo, "--search", o.query, "--state", o.state || "all", "--limit", o.limit || "20", "--json", "number,title,state,url,labels,updatedAt"]);
|
|
113
|
+
return { ...plan, issues, exhaustive: false };
|
|
114
|
+
}
|
|
115
|
+
if (operation === "issue-create") {
|
|
116
|
+
required(o, ["title", "body_file"]);
|
|
117
|
+
Object.assign(plan, { title: o.title, body_file: o.body_file, labels: o.labels });
|
|
118
|
+
if (!o.apply) return plan;
|
|
119
|
+
const body = await readText(o.body_file);
|
|
120
|
+
const args = ["issue", "create", "--repo", o.repo, "--title", o.title, "--body-file", o.body_file];
|
|
121
|
+
for (const label of o.labels) args.push("--label", label);
|
|
122
|
+
// Optional marker enables exact retry recovery for callers with durable ledgers.
|
|
123
|
+
const find = async () => {
|
|
124
|
+
if (!o.marker) return null;
|
|
125
|
+
must(body.includes("<!-- " + o.marker + " -->"), "body must include marker");
|
|
126
|
+
const list = await pages("repos/" + o.repo + "/issues?state=all");
|
|
127
|
+
const found = list.filter(i => !i.pull_request && String(i.body ?? "").includes("<!-- " + o.marker + " -->"));
|
|
128
|
+
must(found.length < 2, "multiple issues with marker");
|
|
129
|
+
return found.length ? readIssue(o.repo, String(found[0].number)) : null;
|
|
130
|
+
};
|
|
131
|
+
const previous = await find();
|
|
132
|
+
if (previous) { must(previous.body === body && previous.title === o.title, "existing issue content drift"); return { ...plan, status: "already-present", url: previous.url }; }
|
|
133
|
+
let url;
|
|
134
|
+
try { url = await run(args); } catch (error) { const recovered = await find(); if (!recovered) throw error; url = recovered.url; }
|
|
135
|
+
const number = /\/issues\/(\d+)$/.exec(url)?.[1]; must(number, "unknown issue create result");
|
|
136
|
+
const after = await readIssue(o.repo, number);
|
|
137
|
+
must(after.body === body && after.title === o.title, "issue readback mismatch");
|
|
138
|
+
return { ...plan, url: after.url, status: "created" };
|
|
139
|
+
}
|
|
140
|
+
if (operation === "issue-comment-close") {
|
|
141
|
+
required(o, ["number", "comment_file", "marker"]);
|
|
142
|
+
const comment = (await readText(o.comment_file)).trim(), marker = "<!-- " + o.marker + " -->";
|
|
143
|
+
const before = await readIssue(o.repo, o.number);
|
|
144
|
+
const exists = before.comments.some(c => String(c.body).includes(marker));
|
|
145
|
+
const result = { ...plan, number: Number(o.number), url: before.url, state_before: before.state, marker: o.marker,
|
|
146
|
+
comment_required: !exists, close_required: before.state !== "CLOSED", steps: [] };
|
|
147
|
+
if (!o.apply) return result;
|
|
148
|
+
if (!exists) { await run(["issue", "comment", o.number, "--repo", o.repo, "--body", comment + "\n\n" + marker]); result.steps.push("commented"); }
|
|
149
|
+
else result.steps.push("comment-already-present");
|
|
150
|
+
if (before.state !== "CLOSED") { await run(["issue", "close", o.number, "--repo", o.repo, "--reason", o.reason || "completed"]); result.steps.push("closed"); }
|
|
151
|
+
const after = await readIssue(o.repo, o.number);
|
|
152
|
+
must(after.state === "CLOSED" && after.comments.some(c => String(c.body).includes(marker)), "issue close readback mismatch");
|
|
153
|
+
return { ...result, state_after: after.state, status: "closed" };
|
|
154
|
+
}
|
|
155
|
+
if (operation === "pr-create") return createPr(o, plan);
|
|
156
|
+
if (["pr-update", "pr-ready"].includes(operation)) {
|
|
157
|
+
required(o, ["number", "base_sha", "head_sha", "expected_body_sha256"]);
|
|
158
|
+
must(/^[a-f0-9]{64}$/.test(o.expected_body_sha256), "invalid expected-body-sha256");
|
|
159
|
+
const before = await readPull(o.repo, o.number); fixed(o, before);
|
|
160
|
+
must(before.state === "OPEN", "PR is not open");
|
|
161
|
+
if (operation === "pr-ready") {
|
|
162
|
+
if (!o.apply) return { ...plan, number: before.number, ready_required: before.isDraft };
|
|
163
|
+
if (before.isDraft) await run(["pr", "ready", o.number, "--repo", o.repo]);
|
|
164
|
+
const after = await readPull(o.repo, o.number); fixed(o, after);
|
|
165
|
+
must(!after.isDraft, "PR remained draft");
|
|
166
|
+
return { ...plan, status: "ready", ...after };
|
|
167
|
+
}
|
|
168
|
+
required(o, ["title", "body_file", "marker"]);
|
|
169
|
+
const body = await readText(o.body_file), marker = "<!-- " + o.marker + " -->";
|
|
170
|
+
must(before.body.includes(marker) && body.includes(marker), "PR marker mismatch");
|
|
171
|
+
if (!o.apply) return { ...plan, number: before.number, body_sha256: sha256(body) };
|
|
172
|
+
await run(["pr", "edit", o.number, "--repo", o.repo, "--title", o.title, "--body", body]);
|
|
173
|
+
const after = await readPull(o.repo, o.number);
|
|
174
|
+
fixed({ ...o, expected_body_sha256: sha256(body) }, after);
|
|
175
|
+
must(after.title === o.title, "PR title readback mismatch");
|
|
176
|
+
return { ...plan, status: "updated", ...after };
|
|
177
|
+
}
|
|
178
|
+
throw new Error("unknown operation: " + operation);
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
export function parseArgs(argv) {
|
|
182
|
+
const [operation, ...rest] = argv, options = { labels: [] };
|
|
183
|
+
const allowed = new Set(["repo", "number", "query", "state", "limit", "title", "body_file", "comment_file", "marker", "reason", "base", "head", "head_repo", "base_sha", "head_sha", "expected_body_sha256", "label"]);
|
|
184
|
+
for (let i = 0; i < rest.length; i++) {
|
|
185
|
+
if (rest[i] === "--apply") { options.apply = true; continue; }
|
|
186
|
+
must(rest[i].startsWith("--"), "unexpected argument");
|
|
187
|
+
const k = rest[i].slice(2).replaceAll("-", "_"), value = rest[++i];
|
|
188
|
+
must(allowed.has(k) && value !== undefined && !value.startsWith("--"), "invalid option or missing value");
|
|
189
|
+
if (k === "label") options.labels.push(value); else { must(!(k in options), "duplicate option"); options[k] = value; }
|
|
190
|
+
}
|
|
191
|
+
return { operation, options };
|
|
192
|
+
}
|
|
193
|
+
export const usage = `github-transport.mjs <operation> --repo OWNER/REPO [options]
|
|
194
|
+
issue-read | pr-read --number N
|
|
195
|
+
issue-search --query TEXT [--state all] [--limit 20]
|
|
196
|
+
issue-create --title TEXT --body-file PATH [--label LABEL] [--marker ID] [--apply]
|
|
197
|
+
issue-comment-close --number N --comment-file PATH --marker ID [--reason completed] [--apply]
|
|
198
|
+
pr-create --base BRANCH --head BRANCH|OWNER:BRANCH [--head-repo OWNER/REPO] --base-sha SHA --head-sha SHA --title TEXT --body-file PATH --marker ID [--apply]
|
|
199
|
+
pr-update --number N --base-sha SHA --head-sha SHA --expected-body-sha256 HASH --title TEXT --body-file PATH --marker ID [--apply]
|
|
200
|
+
pr-ready --number N --base-sha SHA --head-sha SHA --expected-body-sha256 HASH [--apply]
|
|
201
|
+
Defaults to dry-run; caller owns authorization and state. --apply never implies push, merge or release.`;
|
|
202
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
203
|
+
try {
|
|
204
|
+
const { operation, options } = parseArgs(process.argv.slice(2));
|
|
205
|
+
if (!operation || ["--help", "-h", "help"].includes(operation)) console.log(usage);
|
|
206
|
+
else console.log(JSON.stringify(await createTransport()(operation, options), null, 2));
|
|
207
|
+
} catch (error) { console.error("ERROR: " + error.message); process.exitCode = 1; }
|
|
208
|
+
}
|
|
@@ -20,6 +20,9 @@ close_capability: not-applicable
|
|
|
20
20
|
- **Labels or classification supplied by source:** none / ...
|
|
21
21
|
- **Attachments:** none / URL references only
|
|
22
22
|
- **Redactions:** none / description of omitted sensitive material
|
|
23
|
+
- **Pagination / fetched at:** GitHub 来源填完整性、每类条数及读取时间;其他来源不适用
|
|
24
|
+
- **Base / head SHA:** PR 填固定值,其他来源不适用
|
|
25
|
+
- **Supersedes:** 新快照在 frontmatter 填 supersedes_source 指向前一不可变快照;初次来源不适用
|
|
23
26
|
|
|
24
27
|
## Original Content
|
|
25
28
|
|
|
@@ -28,3 +31,7 @@ close_capability: not-applicable
|
|
|
28
31
|
## Source Comments
|
|
29
32
|
|
|
30
33
|
<按来源顺序保存评论;没有评论时写“无”。>
|
|
34
|
+
|
|
35
|
+
## PR Evidence
|
|
36
|
+
|
|
37
|
+
PR 来源保存 reviews、review comments、文件清单和完整 diff;分页缺失或 SHA 漂移阻塞分诊。非 PR 来源写不适用。
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: 1
|
|
3
|
+
artifact: triage-run
|
|
4
|
+
id: TRI-001
|
|
5
|
+
mode: release-preflight
|
|
6
|
+
status: planned
|
|
7
|
+
repo: owner/repo
|
|
8
|
+
change: null
|
|
9
|
+
commit_sha: null
|
|
10
|
+
npm_target: unknown
|
|
11
|
+
targets: []
|
|
12
|
+
updated_at: <ISO-8601>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# 独立操作记录
|
|
16
|
+
|
|
17
|
+
## Targets
|
|
18
|
+
明确 repo、run/alert、workflow、SHA、tag;发布时逐包列 package、registry、version、dist-tag、required/not-required。
|
|
19
|
+
同步填写 frontmatter targets 数组,每项为 {"kind":"npm|github-release|ci|security","identity":"准确包名/tag/run/alert","required":true,"version":null,"registry":null,"dist_tag":null,"observed":"pending|verified|failed|unknown","receipt":null}。npm required 项必须填 version、registry、dist_tag;回读后填写 receipt 证据引用。预检通过允许 pending;release completed 要求每个 required 项 verified 且有 receipt。
|
|
20
|
+
|
|
21
|
+
## Plan
|
|
22
|
+
操作步骤与完成标准。恢复沿用本记录,保留先前尝试。
|
|
23
|
+
|
|
24
|
+
## Evidence
|
|
25
|
+
源证据、命令/退出码、脱敏日志、预检与目标判定理由;无法读取为 unknown。
|
|
26
|
+
|
|
27
|
+
## Authorization
|
|
28
|
+
每个副作用的用户授权出处、目标和内容;尚无授权时只准备计划。
|
|
29
|
+
|
|
30
|
+
## Receipts
|
|
31
|
+
逐步回执与执行后读回;completed 必须列出每个所需目标的确认结果。
|
|
32
|
+
|
|
33
|
+
## Recovery
|
|
34
|
+
失败点、已完成动作、状态未知项、漂移与恢复条件。
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
schema_version:
|
|
2
|
+
schema_version: 2
|
|
3
3
|
artifact: triage
|
|
4
4
|
change: <YYYY-MM-DD-topic>
|
|
5
5
|
mode: intake
|
|
6
|
+
disposition: needs-triage
|
|
7
|
+
verification: pending
|
|
8
|
+
remote_actions: []
|
|
6
9
|
source: <Path>{roots.state}/specdev/changes/{change}/source.md</Path>
|
|
7
10
|
classification: investigation
|
|
8
11
|
risk: medium
|
|
@@ -88,3 +88,10 @@ W 的 `<Path>{roots.state}/specdev/changes/{change}/initiative.json</Path>` 只
|
|
|
88
88
|
新 Ticket/普通 Map 保持原 schema_version,并使用 plan_contract_version: 1 扩展。Ticket 拥有经过核实的 Skill 调用绑定、语义资源和执行计划;Map 路由是其投影。规则为 `<Path>{roots.workflows}/specdev/common/rules/skill-invocation.md</Path>`。计划、产物数量与完成证据漂移必须由对应 owner 修订;不能仅改 map 状态。
|
|
89
89
|
|
|
90
90
|
父 `<Path>{roots.state}/specdev/changes/{change}/tickets-map.md</Path>` 是 goal-tickets-map 无状态入口,只引用现有 Implementation Map/Plan;它不能拥有第二份 status、owner 或任务清单。统一 P 拥有生命周期,旧 O 仅保留入口与恢复键。
|
|
91
|
+
|
|
92
|
+
## 新增工件 owner
|
|
93
|
+
|
|
94
|
+
- T:<Path>{roots.state}/specdev/changes/{change}/pull-requests/PR-###.md</Path> 拥有 PR 交付回执;<Path>{roots.state}/specdev/triage-runs/TRI-###.md</Path> 拥有独立远程操作,绝不复用 external_action/publish_action/capture。
|
|
95
|
+
- R-retro:<Path>{roots.state}/specdev/changes/{change}/retro/RETRO-###.md</Path> 拥有活动复盘建议和来源;不成为永久知识。
|
|
96
|
+
- P:<Path>{roots.state}/specdev/changes/{change}/prototypes/LOGIC-NNN/logic.md</Path> 记录逻辑模型与浏览器证据;index.html 是可运行原型,ready 时 hash 与记录一致。
|
|
97
|
+
- 来源更新:<Path>{roots.state}/specdev/changes/{change}/source.md</Path> 冻结后保持不变。T 将后续读取写入 <Path>{roots.state}/specdev/changes/{change}/sources/SRC-###.md</Path>,记录 replaces、固定 SHA/hash、完整性和原 locator,triage 正文选择有效快照;不得把新远程事实自动升级为已接受 Spec。
|
|
@@ -32,3 +32,9 @@ Owner 原子更新 `<Path>{roots.state}/specdev/changes/{change}/.status.json</P
|
|
|
32
32
|
远程动作不参与本地完成判定。Triage `external_action` 为 `pending-close`/`close-failed` 时先 reconcile;`closed`、`waived` 或 `not-applicable` 才允许 Archive。Triage `publish_action` 为 `pending`/`publish-failed` 时先恢复或结束 publish;`not-requested`、`published` 或 `waived` 才允许 Archive。`not-requested` 是默认,未点过 publish 的 change 不被新模式绑架。归档后工件只读。
|
|
33
33
|
|
|
34
34
|
**完成标准**:完成声明可由本地工件、Git 与验证重建;只有一个 owner 命中;失败 candidate 不污染父分支。
|
|
35
|
+
|
|
36
|
+
## 非实现复盘终点与 PR 归档门
|
|
37
|
+
|
|
38
|
+
仅有 R-retro(可先经 T intake)的 review-only change,以 completed 的 RETRO 报告作为等价验收;不能同时存在 Spec、Ticket、Map、Goal Plan、implementation 工件、Evidence 或 worktree。报告 sources 非空、结果诚实、全部验收完成,owner 按原完成转换规则更新状态;存在实现工件时仍走原实现完成门。insufficient-evidence 仅表示报告完整,不表示问题已解决。
|
|
39
|
+
|
|
40
|
+
PR requested=true 且 status=planned/failed 时阻塞归档;delivery_target=ready 时 draft 也保持未闭合。draft 目标可由 draft/ready 满足,ready 目标只由 ready 满足;waived 须记录放弃交付的授权与原因。创建成功不等于已授权 merge。独立 TRI 不因关联 change 就参与完成/归档门。
|
|
@@ -24,3 +24,9 @@
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
本地已清晰请求不强制绕行 Triage。完成后要记账才选 publish;尚未成 Change、只想先留 inbox 记录才选 capture。publish、reconcile 与 capture 写不同远程对象,可先后执行。G 是单 change Grill,P-prototype 是 UI 设计,不与统一 P-goal-plan 混淆。W 发现候选后逐个交 G/S/T;单 change 拆票后可交 P 规划,多个 child 创建父 Goal 前仍须全部 Ready。原缺陷诊断 D、风险分诊、远程 reconcile、完成后发布投影和记事项捕获职责不删除。
|
|
27
|
+
|
|
28
|
+
## 复盘与交付路由
|
|
29
|
+
|
|
30
|
+
开发环境/流程复盘 → <Path>{roots.workflows}/specdev/R-retro/R-retro.md</Path>。代码讲解仍为 L;Speculo 产品反馈仍为 retro command。CI/安全/PR/包发布 → <Path>{roots.workflows}/specdev/T-triage/T-triage.md</Path> 的对应模式;确定性修复回到 D/I,不由 T 接管代码。
|
|
31
|
+
|
|
32
|
+
长任务在阶段边界按产物与待决问题选择继续、压缩上下文或 handoff;交接只携带准确 owner、SHA、输入和未闭合动作,不硬编码特定模型的上下文阈值,不自动启动其他会话。
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
|
|
58
58
|
## 状态字段
|
|
59
59
|
|
|
60
|
-
`<Path>{roots.state}/specdev/status.json</Path>` 使用全局 schema v5;Spec/Ticket/Tickets Map 保留各自 schema v3;新 Ticket/Map 使用附加 plan_contract_version: 1,config 使用 schema
|
|
60
|
+
`<Path>{roots.state}/specdev/status.json</Path>` 使用全局 schema v5;Spec/Ticket/Tickets Map 保留各自 schema v3;新 Ticket/Map 使用附加 plan_contract_version: 1,config 使用 schema v6,Goal Plan 使用 schema v6,Implementation Map/Plan 使用 schema v1,`<Path>{roots.state}/specdev/changes/{change}/.status.json</Path>` 使用 schema v6:
|
|
61
61
|
|
|
62
62
|
- `schema_version`(数字):全局状态 schema 版本,固定为 `5`。
|
|
63
63
|
- `workflow`(字符串):workflow 标识,固定为 `"specdev"`。
|