Skip to content

Latest commit

 

History

History
86 lines (65 loc) · 13.9 KB

File metadata and controls

86 lines (65 loc) · 13.9 KB

决策日志(DECISIONS.md)

记录关键决策与理由(STA-11)。新决策先在此登记,再实现。

D-001 out-of-tree bundle,不在 harness 仓库内

  • 决定:agent-discipline 为独立仓库(本地文件夹名 dsh-methodology)的 out-of-tree bundle(dsh plugin add 安装)。
  • 理由:脱离 harness 预发布期(0.1.1-rc.2)的仓库抖动与 private workspace 约束;独立版本与发布节奏;harness 官方支持外部 bundle(app-boot 的 resolveBundleDir + 扁平 node_modules 愈合)。

D-002 形态 B(提示段 + 双工具)优先,形态 C 缓行

  • 决定:MVP = persona 提示段 + agent_discipline_init + agent_discipline_audit;暂不做 ctx.features 事件溯源服务。
  • 理由:形态 B 半天-1 天可落地并先跑 S1-S3 对比实验(§11.2);形态 C 需 1-2 周且应在有真实任务数据后再决策。

D-003 相对导入用 .ts 扩展名 + rewriteRelativeImportExtensions

  • 决定:与 harness 仓库约定一致(.ts in local relative imports,tsconfig 开启 rewriteRelativeImportExtensions)。
  • 理由:TS 5.7+ 支持发射时改写为 .js;保持 ESM + NodeNext 可运行。

D-004 纯逻辑层与 harness 接线层分离

  • 决定:src/audit/core.ts 与 src/init/templates.ts 零 harness 依赖;dsh 依赖只出现在 src/tools 与 src/index.ts。
  • 理由:单测可在未链接 harness 依赖时运行(ARCH-02 固化)。

D-005 工具预期失败返回结构化结果而非 throw

  • 决定:agent_discipline_init/audit 对预期失败(目录不存在、文件不可读等)返回 { ok:false, error },不抛异常。
  • 理由:对齐 harness 工具结果契约(预期失败 resolve、基础设施失败 reject)。

D-006 依赖版本范围 >=0.1.0-0

  • 决定:peer/dev 依赖用宽松预发布兼容范围,首次 REAL-composition 装载(F-006)时锁定实际版本。
  • 理由:harness 处于开发者预览期,版本可能漂移。

D-007 命名注册表双视图

  • 决定:src/constants.ts 为代码面唯一出处;naming-registry.md 为仓库工件清单的注册视图;feature_list.json 的 status_legend 引用同一词表。
  • 理由:方法论 §14.1 防词汇漂移;ARCH-03 固化。

D-008 out-of-tree 链接用 link: 协议而非 file:

  • 决定:devDependencies 用 link:../deepseek-harness/... 链接 harness 包,并在 package.json 的 pnpm.autoInstallPeers=false 禁用 peer 自动拉取。
  • 理由(实测):file: 会触发 pnpm 解析被链接包自身的依赖,vendored cordis 的 manifest 用 workspace:^ 声明 cosmokit——在插件仓库这个"外来 workspace"里无法解析,报 ERR_PNPM_WORKSPACE_PKG_NOT_FOUND;link: 是纯符号链接,不解析被链接包依赖,传递类型沿 realpath 走进 harness 检出自己的 node_modules 解析。

D-009 defineTool 的 output 为必填且 ObjectValueSchemaSpec 需 additionalProperties

  • 决定:工具声明 output: { schema, render },对象 schema 必须带 additionalProperties: false
  • 理由(实测):无 output 时 execute 返回类型推断为 Promise;对象 schema 缺 additionalProperties 时类型不匹配。签名对照 harness packages/core/tools/src/schema.ts(DefineToolOptions<S,O>)。

D-010 out-of-tree bundle 补丁 = 顶层数组;新增行用 - insert:(无 id)

  • 决定:cordis.patch.yml 为顶层 YAML 数组(cordis-plugin-include 的 PatchOptions[]);新增插件行用 - insert: [ {id, name}, ... ](无 id 的 insert 会 push 进顶层条目列表)。
  • 理由(实测):in-repo bundle 用顶层 insert: 对象,但 out-of-tree 的 dsh plugin add overlay 由 app-boot 的 parsePatchList 解析,要求顶层数组;无 insert 的行是 id 定向覆盖(目标不存在 → per-entry warning)。

D-011 插件行模块标识键是 name,不是 plugin

  • 决定:补丁行用 name: <包名或子路径> 标识模块。
  • 理由(实测):loader 的 EntryOptions.name 是模块 specifier;写 plugin: 时该字段为 undefined,boot 报 Cannot read properties of undefined (reading 'startsWith')

D-012 invariant 伴生可选装配

  • 决定:bundle 默认只插入主插件行;agent-discipline/invariant 伴生保留导出但进 bundle(要用的组合须同时装载 @deepseek-ai/dsh-invariants)。
  • 理由(实测):inject: ['invariants'] 让该行在无 invariants 服务的组合(产品 bundle,如 dsh-base)里永久 pending,app-boot 的 assertEntriesActivated fail-loud 拒绝启动;invariants 仅开发/测试组合装载。

D-013 命名:包名/插件标识 = agent-discipline,文件夹名保留 dsh-methodology

  • 决定:npm 包名、插件 name、bundle 行 id、提示段名、工具名(agent_discipline_init/audit)、invariant 名统一为 agent-discipline;本地文件夹名与仓库目录保持 dsh-methodology 不变(远端 GitHub 仓库名独立于文件夹名)。
  • 理由:插件名"methodology"不像产品名,且"dsh"前缀暗示专属 DeepSeek Harness(方法论源自开源课程,见 README 致谢);agent-discipline 描述"给编码 Agent 的仓库工作加纪律"的实际职责。

D-014 审计启发式增强(C5 多生态锁文件、C6 CI 与命令别名)

  • 决定:C5 锁文件候选扩展至 go.sum / Gradle(gradle.lockfile、dependencies.lock)/ Maven(pom.xml 版本钉定视作等价)/ pipenv(Pipfile.lock)/ uv / composer / Gemfile / packages.lock.json / bun;C6 增加 CI 配置证据(.github/workflows 目录枚举、.gitlab-ci.yml、.circleci、azure-pipelines、Jenkinsfile)与常见验证命令别名表(make verify、mvn verify、gradle check、go test ./...、cargo test、pytest 等)。
  • 理由(实测):原实现只认 pnpm/package-lock/yarn/Cargo/poetry 与 scripts/check.sh|check.sh|Makefile,CI-only 与 Go/Java 仓库会误报失败;增强后显著改善对已有项目(brownfield)的适用性。检查仍为启发式,不执行目标仓库的验证命令本身。

D-015 F-006 增强先务实版(进程内组合测试),完整版(真实 Loader)缓行

  • 决定:先落 tests/composition.spec.ts——vitest 内 new Context() + ctx.plugin(SystemPrompt, { includeRuntimeContext: false }) + ctx.plugin(ToolRuntime) + ctx.plugin(agent-discipline 命名空间),断言:① assemble() 后提示段位于 harness:identity(-100) 与 deployment:persona(0) 之间、文本含「会话生命周期」与工具名;② tools 注册表恰好含 agent_discipline_init/audit 两个工具;真实 Loader 解析 patch 行的完整版留作后续。
  • 理由:务实版覆盖"注册逻辑"全部断言面,只依赖两个服务的公开接口(section/assemble/get/schemas),稳、快、进 npm run check 常驻(L2);完整版额外覆盖"Loader 补丁解析"层,目前有 CLI boot + --dump-config 证据兜底,且依赖 harness 内部 API(Loader 导出名/app-boot 用法)更脆弱,放后续增强。
  • 后续(已落地):完整版 = 复用 @deepseek-ai/dsh-app-bootboot()(与 dsh 二进制同一启动路径)+ loadOverlayPatches(解析本包 cordis.patch.yml)+ fixture cordis.yml + node_modules/agent-discipline junction(复刻 plugin add 的链接);见 tests/loader-composition.spec.ts,vitest 19/19 全绿,D-010/D-011/D-012 从"实测记录"升级为机器断言。

D-016 形态 C 上马:分期 C-1/C-2/C-3,真源取「会话日志 + feature_list 导出投影」

  • 决定:四轮实验(R1/R2/R3 + S2/S3,见 docs/experiments/s1-s3.md)数据支撑上形态 C。C-1ctx.features 服务(create/update/transition/verify + CAS 修订号)+ 严格 fold(WIP=1 违约、passing 无证据、非法状态跳转 → corrupt)+ 工具 get_feature/update_feature/verify_feature(替代手改 JSON);C-2:真实 invariant(会话增量折叠 + internal/dispatch 预提交拦截);C-3(可选):GUI 看板/投影单元,C-1/C-2 稳定后再定。状态真源取方案甲:会话日志为真源 + feature_list.json 作导出投影(README 演进路径推荐;贴合 OBS-04「模型可见⟺可日志化」)。状态词表不变(not_started/in_progress/blocked/passing,§3.3)。
  • 理由(实验证据):R3 实证形态 B 验证门可被绕过(T3 的 B 会话未执行验证门、T1/T2 的 A 带病交付——模型自觉性波动 ≥ 纪律约束力),人工介入 A 3 次 vs B 1 次;S2 工件定位价值成立(B 4/4 vs A FAIL)。形态 C 买的是机器强制、不可绕过与防 AP-09/AP-10 的结构性保障,不是完成率(R3-T4 自觉会话无增量,如实记录)。分期理由:C-1 独立交付独立验证,不背完整事件溯源复杂度;C-2 的 dispatch 拦截点与 C-3 的投影都依赖 C-1 的服务先立住。

D-017 形态 C-1 真源修订:插件自管事件日志(不依赖 harness 会话日志)

  • 决定:C-1 真源从 D-016 的「harness 会话日志」修订为「插件自管事件日志」(.harness/features/events.jsonl,append-only)+ feature_list.json 导出投影。
  • 理由(实测边界缺口):harness 的 live session.append 不支持 ignorable 标记(opts 仅对 surface 事件开放);KNOWN_SESSION_EVENT_TYPES 是 harness 构建时生成的枚举,注释明说 out-of-repo 插件事件 by construction 不在其中、且 event-name registration 被拒绝——feature/change 若不标 ignorable,跨会话重启时 harness 持久化读路径会拒绝解释整个日志(coordinator.ts "refusing to interpret the log")。自管事件日志完全规避该边界:事件溯源精神保留(真源 + 投影)、零 harness 改动、CAS 天然(append-only 日志单调)、机器强制更强(手改 feature_list.json 只是改投影,被真源折叠覆盖)。改 harness(session.append 加 ignorable)留作可选后续(C-2/C-3 若需与 harness 会话日志集成再评估)。

D-018 形态 C-3 分层:投影单元 + 模型可见视图(实施),GUI 看板(暂缓)

  • 决定:C-3(F-011)拆两层——C-3a 实施:投影单元固化(FeatureService.exportProjection:显式导出 feature_list.json + 一致性校验,复用 checkFeatureLogHealth)与模型可见状态视图(feature_dashboard 工具:折叠汇总输出,含各状态计数/WIP 占用/证据摘要/投影一致性,符合 OBS-04「模型可见⟺可日志化」);C-3b 暂缓:GUI 特性看板。
  • 理由:GUI 看板技术上可行(harness 有完整 dsh.client 双面插件机制:client-modules 扫描声明 dsh.client 的包 → 组装 client entry graph → 注入 window.__DSH_BOOT__;ui-goal 是蓝本——React 组件 + ctx.slots.inject/register 槽位 + locale + client 构建链 tsdown.client),但成本 = React 客户端工程 + client 构建链 + 我们的 bundle 引入前端依赖 + web profile 集成测试,且与形态 C 核心价值(机器强制、不可绕过)正交——看板只增强"可见性"。模型可见状态视图以零成本覆盖同一信息需求;GUI 看板待形态 C 整体稳定后单独评估。

D-019 ScanApp 实测暴露:形态 B→C 迁移(importExisting)+ 工具输出 lossless 修复

  • 决定:FeatureService.importExisting() 把存量 feature_list.json(事件日志为空时)逐条导入为事件(not_started→create;in_progress/blocked→create+transition;passing→create+transition+verify,evidence 数组 join);update_feature 工具加 importExisting 操作;工具输出 out() 剔除 undefined 字段(修复实测的 "value is not lossless JSON")。
  • 理由(ScanApp 实测):形态 C 接管存量仓库时,投影重建会覆盖手写的 feature_list.json 且丢失未登记条目(实测中 F-001..F-003 曾从投影消失,git checkout 恢复后经迁移重入事件日志——7 行事件);工具输出含 undefined 字段(blockedReason 等)在 harness output 规范化下非 lossless JSON。
  • harness 环境问题(记录,非本插件缺陷):0.1.2 的 dsh-plugin-package-inventory-deepseek 在 headless 组合里 bare 包解析找不到 flat fallback($DSH_HOME/profiles/node_modules)→ REQUEST_EXTENSION;实测 overlay 以 disabled: true 绕过(不改 harness/用户 profile),建议上报 harness。

D-020 web 会话 root 解析修复(F-013,问题报告:FeatureService root 固化缺陷)

  • 决定:root 解析从「构造时固化」改为「per-call 解析」——FeatureService.resolveRoot(session?):显式 config.root → 会话 session.header.cwdsandboxPolicy.resolve({ session }) 的 per-session 路径)→ 构造回退根(静态 workspaceRoot → process.cwd());四个特性工具与 audit/init 工具的 execute(args, exec) 接收 ToolRunContext,把 exec.agent?.session 传入;get_feature 未找到返回 { feature: null }(oneOf object|null schema);agent_discipline_audit output schema 补全 scores/weakest/failedCritical(裁剪非承诺的 results,README「结构化字段」契约对齐)。
  • 理由(问题报告,web GUI @ 3080 会话实测复现):web 多会话部署下 sandboxPolicy.workspaceRoot 静态值 = harness 服务进程目录(config.workspaceRoot ?? process.cwd()),只有 per-session 解析才用 session.header.cwd;原实现构造时固化静态根 → 工具读不到 ScanApp 状态(get_feature 空列表、importExisting 误报「feature_list.json 不可读」、audit 无 target 审计错目录且 schema 缺字段触发 lossless 拒绝)。ScanApp 历史事件由 headless CLI 会话写入(当时 process.cwd() 恰好 = ScanApp)——web 复现失败,恰证 per-session 缺失。init 工具同根因一并修复(D-020 范围扩展)。
  • 附带:本仓库自身触发形态 B→C 迁移(session-handoff 触发条件①:本仓库真实会话第一次用 feature 工具登记前 importExisting)——12 条存量全部导入事件日志(32 行),投影重建含全部条目 + F-013;本仓库自证审计通过(audit 无 target 按会话 root 100/100)。