easy-coding-harness 0.9.1 → 0.10.0-beta.1

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/CHANGELOG.md CHANGED
@@ -6,6 +6,37 @@
6
6
  - `y`:常规功能升级
7
7
  - `z`:日常 bug 修复
8
8
 
9
+ ## 0.10.0-beta.1
10
+
11
+ - 新增默认关闭的 Java TDD 模式,支持项目级 CLI 配置与 session 覆盖;覆盖率阈值默认
12
+ 90、可配置 1..100,并在 ANALYSIS → IMPLEMENT 与任务一起冻结。
13
+ - 新增 `ec-config` 统一管理 Approval、Workflow、TDD 与阈值;`ec-task-management` 收口为
14
+ 纯任务生命周期面板。TDD 开启时状态栏在 Workflow 后显示独立 `TDD` 标识,关闭时格式
15
+ 与原有执行深度完全不变。
16
+ - TDD 开启后要求 Java RED/GREEN/REFACTOR(纯重构为 characterization GREEN → GREEN)、
17
+ 独立 TDD review,以及 JaCoCo 修改生产代码可执行行差异覆盖率;本地与 GitLab TEST
18
+ stage 复用 `.easy-coding/tools/easy_coding_java_coverage.py` 门禁,验收必须同时保留本地
19
+ 结果与成功 GitLab pipeline/job 证据。
20
+ - 配置 schema 升至 4;旧项目、session 与在途 task 迁移后保持 TDD 关闭。TDD 关闭时不
21
+ 扫描 CI/JaCoCo、不生成 TDD artifact、不运行额外命令,也不提高 Fast/Standard/Strict
22
+ 的既有测试与验收深度。
23
+
24
+ ## 0.10.0-beta.0
25
+
26
+ - 接入 `easy-dev-spec/v1` Canonical Spec:同步最终 producer 协议实现并新增只读
27
+ `inspect-dev-spec`、确定性 `select-dev-spec-scope`、选择式 `create-task-from-spec` 与
28
+ 依赖证据 `satisfy-spec-dependency` 状态 API;用户选择多个 Spec task 后仍只创建一个
29
+ Harness task,消费上下文按仓库形成闭包。
30
+ - Task、Unit 与执行证据新增来源 Spec SHA、仓库、source task/step、符号和测试命令追踪;
31
+ ANALYSIS 使用最终 READY 语义机械校验全局、契约、仓库、任务和验收闭环,并继续校验
32
+ 仓库身份、基线、hard 依赖及 task/change/step/test 追踪。
33
+ - IMPLEMENT、REVIEW、VERIFICATION 与 Git 范围按 `repo_id` 隔离;pending integration
34
+ 依赖允许完成本地检查,但在证据闭合前禁止进入 MEMORY 或宣称全链路完成。
35
+ - 保持历史/legacy task、现有状态图、approval/workflow mode 与指纹证据兼容;Canonical
36
+ Spec 始终只读,本地 dev-spec、execution plan 和 test strategy 作为带来源哈希的派生物。
37
+ - 补齐 Codex 根身份兼容:真实运行时写入的裸 `root`、既有 `/root` 及其协作子路径统一
38
+ 规范化为 `codex`,避免状态行、breadcrumb、任务列表与 claim 再次误报跨 Agent handoff。
39
+
9
40
  ## 0.9.1
10
41
 
11
42
  - 正式发布 Codex Agent 身份归一化修复:协作路径 `/root`、`/root/...` 与平台身份
package/README.md CHANGED
@@ -77,6 +77,7 @@ INIT --[always auto]--> ANALYSIS -> IMPLEMENT -> REVIEW -> VERIFICATION -> MEMOR
77
77
  +------- repair ----------+
78
78
  approval --[approve / guard / confirm / auto]--> transition wait policy
79
79
  workflow --[adaptive => fast / standard / strict]--> stage execution depth
80
+ tdd --[off by default / Java changed-line gate]--> optional test discipline
80
81
  any stage --[user abort via ec-task-close]--> CLOSED
81
82
  ```
82
83
 
@@ -85,6 +86,7 @@ any stage --[user abort via ec-task-close]--> CLOSED
85
86
  ANALYSIS → IMPLEMENT 确认一次,随后各阶段在质量门禁通过后自动推进,`auto` 从开始即
86
87
  自动推进。
87
88
  - 工作流模式优先级为 session 覆盖 > 项目 `behavior.workflow_mode` > `adaptive`。Adaptive 在 ANALYSIS 结束时根据风险解析、展示并冻结为 `fast`、`standard` 或 `strict`,用户可在风险下限之上调整。
89
+ - Java TDD 默认关闭;优先级为 session 覆盖 > 项目配置 > `false/90%`。开启后在 ANALYSIS → IMPLEMENT 冻结开关与阈值,执行 RED/GREEN/REFACTOR(纯重构使用 characterization GREEN → GREEN),并以同一工具执行本地与 GitLab TEST-stage 差异覆盖率门禁。关闭时不扫描 CI/JaCoCo、不增加命令或提高原工作流验收深度。
88
90
  - 所有新代码任务都完整进入 REVIEW;不同工作流模式只调整各状态内部的上下文加载、执行主体、审查独立性、验证范围和记忆深度,不绕过状态或证据门禁。
89
91
  - 显式 `doc` / `analysis` / `report` 只读任务不生成 `test-strategy.md`;展示完整报告后按生效模式进入 COMPLETE,不执行 REVIEW、VERIFICATION 或 MEMORY,也不写任务记忆。
90
92
  - `VERIFICATION` 是验证硬门控:Fast 运行最小充分检查,Standard 运行受影响范围检查,
@@ -92,6 +94,23 @@ any stage --[user abort via ec-task-close]--> CLOSED
92
94
  并留下当前指纹下的绿色证据,就不算通过。
93
95
  - `MEMORY` 先写入本次任务短期记忆,再执行长期记忆阈值门禁;未超过阈值时长期沉淀为 no-op。
94
96
 
97
+ ## Canonical Dev Spec
98
+
99
+ Harness 可选择性消费 easy-dev-spec 生成的单文件 `easy-dev-spec/v1` Canonical Spec:
100
+
101
+ 1. `ec-workflow` 先只读检查 manifest、仓库、任务 DAG、依赖和 baseline,不创建任务。
102
+ 2. 用户明确选择一个或多个 Spec task;`select-dev-spec-scope` 按仓库提取确定性消费
103
+ 闭包,Harness 不默认导入整份 Spec,也不会读取未选任务正文。
104
+ 3. 一次选择创建一个 Harness task;ANALYSIS 使用最终 producer READY 门禁,并将
105
+ selected task 映射为带 `repo_id`、`source_task_id`、source steps、文件、符号和测试
106
+ 命令的 Unit。
107
+ 4. `hard` 依赖决定执行顺序,冻结的 `contract` 依赖允许并行编码,`integration` 依赖
108
+ 在证据闭合前阻止全链路完成。
109
+
110
+ Canonical Spec 是只读设计源;`.easy-coding/tasks/<task-id>/` 下的 `dev-spec.md`、
111
+ `execution.jsonl` 和 `test-strategy.md` 是绑定来源 SHA-256 的运行时派生物,不会把开发
112
+ 进度写回源 Spec。无 Canonical manifest 的历史 Dev-Spec 继续走原有整文分析流程。
113
+
95
114
  ## Supermodule 模型
96
115
 
97
116
  在包含 `.gitmodules` 的父仓中,Easy Coding Harness 按 git 边界分层运行:
@@ -113,7 +132,7 @@ any stage --[user abort via ec-task-close]--> CLOSED
113
132
  | `easy-coding add-agent` | 给已接入项目追加 Claude Code、Codex 或 Qoder 支持;supermodule 父仓可按已初始化子仓分层追加 |
114
133
  | `easy-coding upgrade` | CLI 升级后同步项目内生成文件,生成区覆盖,用户资产保留;supermodule 父仓会同步升级已初始化子仓 |
115
134
  | `easy-coding update` | 更新全局 CLI 到最新发布版 |
116
- | `easy-coding config` | 交互修改当前项目的 `approval_mode` `workflow_mode` |
135
+ | `easy-coding config` | 交互修改当前项目的 Approval、Workflow、Java TDD 与覆盖率阈值 |
117
136
  | `easy-coding status` | 查看已安装平台、harness 版本、当前任务状态 |
118
137
  | `easy-coding clear` | 移除 harness 安装物,保留 tasks、spec、memory、project.yaml 等用户资产;supermodule 父仓支持交互选择、`--submodules` 和 `--no-submodules` |
119
138
 
@@ -130,7 +149,8 @@ any stage --[user abort via ec-task-close]--> CLOSED
130
149
  | `ec-reviewing` | 多维度代码审查,输出 accept / fix / replan / blocked 结论 |
131
150
  | `ec-verification` | 执行 lint、typecheck、test 等验证硬门控,并处理验收修复循环 |
132
151
  | `ec-memory` | 写短期记忆,并在超过阈值时沉淀长期记忆 |
133
- | `ec-task-management` | 任务与 session 面板:查看、创建、选择、恢复任务并管理当前会话确认模式 |
152
+ | `ec-task-management` | 任务面板:查看、创建、选择、恢复、交接任务 |
153
+ | `ec-config` | 只读查看或显式修改项目/session 的 Approval、Workflow、TDD 与阈值 |
134
154
  | `ec-task-close` | 用户主动中断任务并关闭 |
135
155
  | `ec-no-harness` | 当前会话仅旁路 Easy Coding Harness,使用原生 Agent 能力 |
136
156
  | `ec-git` | 约束 git diff、commit、push、跨仓库提交等交付动作 |
@@ -167,7 +187,8 @@ easy-coding upgrade
167
187
  `behavior.approval_mode` 与 `behavior.workflow_mode`;旧 `lite` 映射为 `guard + fast`。
168
188
  项目级模式用 `easy-coding config` 修改(要求
169
189
  项目 Harness 与 CLI 版本完全一致,否则先执行 `easy-coding upgrade` 或更新 CLI);当前
170
- session 的两类临时覆盖通过 `ec-task-management` 对话修改。
190
+ session 临时覆盖统一通过 `ec-config` 对话修改。升级到 0.10.0-beta.1 时配置 schema 升至
191
+ 4,旧项目、会话和在途任务均迁移为 TDD 关闭;同时部署共享 Java 差异覆盖率工具。
171
192
 
172
193
  若当前会话不希望 Harness 接管,显式调用 `/ec-no-harness`(Codex 使用
173
194
  `$ec-no-harness`)。它只旁路 Easy Coding,不关闭其他 hooks,也不忽略其他 skills;
package/dist/cli.js CHANGED
@@ -103,7 +103,8 @@ async function isDirectory(filePath) {
103
103
  }
104
104
 
105
105
  // src/utils/config-yaml.ts
106
- var CONFIG_SCHEMA_VERSION = 3;
106
+ var CONFIG_SCHEMA_VERSION = 4;
107
+ var DEFAULT_TDD_COVERAGE_THRESHOLD = 90;
107
108
  var APPROVAL_MODES = ["approve", "guard", "confirm", "auto"];
108
109
  var CONFIGURED_WORKFLOW_MODES = ["adaptive", "fast", "standard", "strict"];
109
110
  function createDefaultConfig(params) {
@@ -125,7 +126,9 @@ function createDefaultConfig(params) {
125
126
  },
126
127
  behavior: {
127
128
  approval_mode: "guard",
128
- workflow_mode: "adaptive"
129
+ workflow_mode: "adaptive",
130
+ tdd_enabled: false,
131
+ tdd_coverage_threshold: DEFAULT_TDD_COVERAGE_THRESHOLD
129
132
  }
130
133
  };
131
134
  if (params.supermodule) {
@@ -179,31 +182,43 @@ function isApprovalMode(value) {
179
182
  function isConfiguredWorkflowMode(value) {
180
183
  return typeof value === "string" && CONFIGURED_WORKFLOW_MODES.includes(value);
181
184
  }
185
+ function isTddCoverageThreshold(value) {
186
+ return Number.isInteger(value) && Number(value) >= 1 && Number(value) <= 100;
187
+ }
182
188
  function resolveLegacyBehavior(config2) {
183
189
  const behavior = config2.behavior ?? {};
184
190
  const legacyLite = behavior.confirm_mode === "lite";
185
191
  const approvalMode = isApprovalMode(behavior.approval_mode) ? behavior.approval_mode : isApprovalMode(behavior.confirm_mode) ? behavior.confirm_mode : behavior.auto_mode === true ? "auto" : behavior.strict_confirm === true ? "approve" : "guard";
186
192
  const workflowMode = isConfiguredWorkflowMode(behavior.workflow_mode) ? behavior.workflow_mode : legacyLite ? "fast" : "adaptive";
187
- return { approvalMode, workflowMode };
193
+ const supportsTdd = Number(config2.version) >= CONFIG_SCHEMA_VERSION;
194
+ const tddEnabled = supportsTdd && behavior.tdd_enabled === true;
195
+ const tddCoverageThreshold = supportsTdd && isTddCoverageThreshold(behavior.tdd_coverage_threshold) ? behavior.tdd_coverage_threshold : DEFAULT_TDD_COVERAGE_THRESHOLD;
196
+ return { approvalMode, workflowMode, tddEnabled, tddCoverageThreshold };
188
197
  }
189
- async function setBehaviorModes(filePath, approvalMode, workflowMode) {
198
+ async function setBehaviorModes(filePath, approvalMode, workflowMode, tddEnabled, tddCoverageThreshold) {
199
+ if (tddCoverageThreshold !== void 0 && !isTddCoverageThreshold(tddCoverageThreshold)) {
200
+ throw new Error("TDD coverage threshold must be an integer from 1 to 100.");
201
+ }
190
202
  return updateConfigYaml(filePath, (config2) => {
191
203
  const legacyBehavior = config2.behavior ?? {};
204
+ const resolvedBehavior = resolveLegacyBehavior(config2);
192
205
  const behavior = Object.fromEntries(
193
206
  Object.entries(legacyBehavior).filter(
194
- ([key]) => key !== "strict_confirm" && key !== "auto_mode" && key !== "confirm_mode" && key !== "approval_mode" && key !== "workflow_mode"
207
+ ([key]) => key !== "strict_confirm" && key !== "auto_mode" && key !== "confirm_mode" && key !== "approval_mode" && key !== "workflow_mode" && key !== "tdd_enabled" && key !== "tdd_coverage_threshold"
195
208
  )
196
209
  );
197
210
  behavior.approval_mode = approvalMode;
198
211
  behavior.workflow_mode = workflowMode;
212
+ behavior.tdd_enabled = tddEnabled ?? resolvedBehavior.tddEnabled;
213
+ behavior.tdd_coverage_threshold = tddCoverageThreshold ?? resolvedBehavior.tddCoverageThreshold;
199
214
  config2.behavior = behavior;
200
215
  config2.version = CONFIG_SCHEMA_VERSION;
201
216
  });
202
217
  }
203
218
  async function migrateBehaviorConfig(filePath) {
204
219
  const config2 = await readConfigYaml(filePath);
205
- const { approvalMode, workflowMode } = resolveLegacyBehavior(config2);
206
- return setBehaviorModes(filePath, approvalMode, workflowMode);
220
+ const { approvalMode, workflowMode, tddEnabled, tddCoverageThreshold } = resolveLegacyBehavior(config2);
221
+ return setBehaviorModes(filePath, approvalMode, workflowMode, tddEnabled, tddCoverageThreshold);
207
222
  }
208
223
  async function ensureProjectId(filePath) {
209
224
  let projectId = "";
@@ -242,6 +257,7 @@ var SPEC_DIR = "spec";
242
257
  var MAIN_SPEC_DIR = "main";
243
258
  var DEV_SPEC_DIR = "dev";
244
259
  var TEMPLATES_DIR = "templates";
260
+ var TOOLS_DIR = "tools";
245
261
  var SESSIONS_GITIGNORE_ENTRY = ".easy-coding/sessions/";
246
262
  var HOOK_BYTECODE_GITIGNORE_ENTRY = "__pycache__/";
247
263
  var GENERATED_REGION_START = "<!-- \u2550\u2550\u2550 easy-coding-harness generated (DO NOT EDIT BETWEEN MARKERS) \u2550\u2550\u2550 -->";
@@ -659,8 +675,15 @@ async function writeRuntimeScaffold(cwd, agents, opts = {}) {
659
675
  await ensureDir(path6.join(easyCodingDir, SPEC_DIR, DEV_SPEC_DIR));
660
676
  await writeMemoryScaffold(easyCodingDir);
661
677
  await writeTemplatesScaffold(easyCodingDir);
678
+ await writeToolsScaffold(easyCodingDir);
662
679
  return projectId;
663
680
  }
681
+ async function writeToolsScaffold(easyCodingDir) {
682
+ const toolsDir = path6.join(easyCodingDir, TOOLS_DIR);
683
+ await ensureDir(toolsDir);
684
+ const src = getTemplatePath("runtime", "tools", "easy_coding_java_coverage.py");
685
+ await writeTextFile(path6.join(toolsDir, "easy_coding_java_coverage.py"), await readTextFile(src));
686
+ }
664
687
  async function writeTemplatesScaffold(easyCodingDir) {
665
688
  const templatesDir = path6.join(easyCodingDir, TEMPLATES_DIR);
666
689
  await ensureDir(templatesDir);
@@ -816,6 +839,17 @@ function migrateTaskWorkflowState(task) {
816
839
  task.workflow_mode_legacy = true;
817
840
  changed = true;
818
841
  }
842
+ if (isActive && taskType !== "project-init" && typeof task.tdd_enabled !== "boolean") {
843
+ task.tdd_enabled = false;
844
+ task.tdd_coverage_threshold = DEFAULT_TDD_COVERAGE_THRESHOLD;
845
+ task.tdd_confirmed_at = (/* @__PURE__ */ new Date()).toISOString();
846
+ task.tdd_confirmed_by = "upgrade-migration";
847
+ changed = true;
848
+ }
849
+ if (task.tdd_enabled === false && "tdd_baselines" in task) {
850
+ task.tdd_baselines = void 0;
851
+ changed = true;
852
+ }
819
853
  return changed;
820
854
  }
821
855
  function migrateSessionBehavior(session) {
@@ -2515,6 +2549,7 @@ function addRuntimeClearEntries(plan, cwd) {
2515
2549
  path16.join(cwd, EASY_CODING_DIR, CONFIG_FILE),
2516
2550
  path16.join(cwd, EASY_CODING_DIR, SESSIONS_DIR),
2517
2551
  path16.join(cwd, EASY_CODING_DIR, TEMPLATES_DIR),
2552
+ path16.join(cwd, EASY_CODING_DIR, TOOLS_DIR),
2518
2553
  path16.join(cwd, EASY_CODING_DIR, INSTALL_MANIFEST_FILE)
2519
2554
  ])
2520
2555
  ];
@@ -2738,7 +2773,7 @@ function renderTargetPlans(targetPlans) {
2738
2773
 
2739
2774
  // src/commands/config.ts
2740
2775
  import path18 from "path";
2741
- import { cancel as cancel4, confirm as confirm3, outro as outro3, select } from "@clack/prompts";
2776
+ import { cancel as cancel4, confirm as confirm3, outro as outro3, select, text } from "@clack/prompts";
2742
2777
  import chalk4 from "chalk";
2743
2778
 
2744
2779
  // src/utils/compare-versions.ts
@@ -2901,16 +2936,48 @@ async function config() {
2901
2936
  cancel4("Configuration cancelled.");
2902
2937
  return;
2903
2938
  }
2939
+ const tddEnabled = await select({
2940
+ message: `Enable Java TDD for this project (current: ${current.tddEnabled ? "enabled" : "disabled"})`,
2941
+ initialValue: current.tddEnabled,
2942
+ options: [
2943
+ { value: false, label: "disabled \u2014 preserve current test depth (default)" },
2944
+ { value: true, label: "enabled \u2014 require TDD evidence and changed-line coverage" }
2945
+ ]
2946
+ });
2947
+ if (typeof tddEnabled === "symbol") {
2948
+ cancel4("Configuration cancelled.");
2949
+ return;
2950
+ }
2951
+ let tddCoverageThreshold = current.tddCoverageThreshold;
2952
+ if (tddEnabled) {
2953
+ const thresholdInput = await text({
2954
+ message: "Minimum changed-production-line coverage percentage",
2955
+ initialValue: String(current.tddCoverageThreshold),
2956
+ validate(value) {
2957
+ const parsed = Number(value);
2958
+ return isTddCoverageThreshold(parsed) ? void 0 : "Enter an integer from 1 to 100.";
2959
+ }
2960
+ });
2961
+ if (typeof thresholdInput === "symbol") {
2962
+ cancel4("Configuration cancelled.");
2963
+ return;
2964
+ }
2965
+ tddCoverageThreshold = Number(thresholdInput);
2966
+ }
2904
2967
  const shouldSave = await confirm3({
2905
- message: `Set behavior.approval_mode to ${approvalMode} and behavior.workflow_mode to ${workflowMode}?`,
2968
+ message: `Set approval=${approvalMode}, workflow=${workflowMode}, TDD=${tddEnabled ? `enabled (${tddCoverageThreshold}%)` : "disabled"}?`,
2906
2969
  initialValue: true
2907
2970
  });
2908
2971
  if (typeof shouldSave === "symbol" || !shouldSave) {
2909
2972
  cancel4("Configuration cancelled.");
2910
2973
  return;
2911
2974
  }
2912
- await setBehaviorModes(configPath2, approvalMode, workflowMode);
2913
- outro3(chalk4.green(`Project modes updated: approval=${approvalMode}, workflow=${workflowMode}.`));
2975
+ await setBehaviorModes(configPath2, approvalMode, workflowMode, tddEnabled, tddCoverageThreshold);
2976
+ outro3(
2977
+ chalk4.green(
2978
+ `Project modes updated: approval=${approvalMode}, workflow=${workflowMode}, TDD=${tddEnabled ? `${tddCoverageThreshold}%` : "off"}.`
2979
+ )
2980
+ );
2914
2981
  }
2915
2982
 
2916
2983
  // src/commands/init.ts
@@ -3218,14 +3285,24 @@ async function status() {
3218
3285
  const migratedBehavior = resolveLegacyBehavior(config2);
3219
3286
  const projectApprovalMode = isApprovalMode(config2.behavior?.approval_mode) ? config2.behavior.approval_mode : migratedBehavior.approvalMode;
3220
3287
  const projectWorkflowMode = isConfiguredWorkflowMode(config2.behavior?.workflow_mode) ? config2.behavior.workflow_mode : migratedBehavior.workflowMode;
3288
+ const projectTddEnabled = migratedBehavior.tddEnabled;
3289
+ const projectTddCoverageThreshold = migratedBehavior.tddCoverageThreshold;
3221
3290
  console.log(` approval_mode: ${projectApprovalMode}`);
3222
3291
  console.log(` workflow_mode: ${projectWorkflowMode}`);
3292
+ console.log(` tdd_enabled: ${projectTddEnabled}`);
3293
+ console.log(` tdd_coverage_threshold: ${projectTddCoverageThreshold}`);
3223
3294
  console.log("");
3224
3295
  console.log(chalk6.bold("Sessions"));
3225
3296
  console.log(` project_approval_mode: ${projectApprovalMode}`);
3226
3297
  console.log(` project_workflow_mode: ${projectWorkflowMode}`);
3298
+ console.log(` project_tdd_enabled: ${projectTddEnabled}`);
3299
+ console.log(` project_tdd_coverage_threshold: ${projectTddCoverageThreshold}`);
3227
3300
  console.log(` effective_approval_mode: ${projectApprovalMode} (without a session override)`);
3228
3301
  console.log(` configured_workflow_mode: ${projectWorkflowMode} (without a session override)`);
3302
+ console.log(` effective_tdd_enabled: ${projectTddEnabled} (without a session override)`);
3303
+ console.log(
3304
+ ` effective_tdd_coverage_threshold: ${projectTddCoverageThreshold} (without a session override)`
3305
+ );
3229
3306
  if (sessions.length === 0) {
3230
3307
  console.log(" no session files");
3231
3308
  }
@@ -3236,13 +3313,21 @@ async function status() {
3236
3313
  );
3237
3314
  const sessionApprovalMode = session.approval_mode ?? (legacySessionMode === "lite" ? "guard" : legacySessionMode);
3238
3315
  const sessionWorkflowMode = session.workflow_mode ?? (legacySessionMode === "lite" ? "fast" : hasLegacySessionMode ? "adaptive" : void 0);
3316
+ const sessionTddEnabled = session.tdd_enabled;
3317
+ const sessionTddCoverageThreshold = session.tdd_coverage_threshold;
3239
3318
  console.log(` - ${key}`);
3240
3319
  console.log(` agent: ${session.agent ?? "legacy/unknown"}`);
3241
3320
  console.log(` source: ${session.session_source ?? "legacy"}`);
3242
3321
  console.log(` approval_mode: ${sessionApprovalMode ?? "project default"}`);
3243
3322
  console.log(` workflow_mode: ${sessionWorkflowMode ?? "project default"}`);
3323
+ console.log(` tdd_enabled: ${sessionTddEnabled ?? "project default"}`);
3324
+ console.log(` tdd_coverage_threshold: ${sessionTddCoverageThreshold ?? "project default"}`);
3244
3325
  console.log(` effective_approval_mode: ${sessionApprovalMode ?? projectApprovalMode}`);
3245
3326
  console.log(` configured_workflow_mode: ${sessionWorkflowMode ?? projectWorkflowMode}`);
3327
+ console.log(` effective_tdd_enabled: ${sessionTddEnabled ?? projectTddEnabled}`);
3328
+ console.log(
3329
+ ` effective_tdd_coverage_threshold: ${sessionTddCoverageThreshold ?? projectTddCoverageThreshold}`
3330
+ );
3246
3331
  console.log(
3247
3332
  ` harness: ${session.harness_disabled ? "disabled for this session" : "enabled"}`
3248
3333
  );
@@ -3258,6 +3343,10 @@ async function status() {
3258
3343
  console.log(
3259
3344
  ` task_workflow_mode: ${task.workflow_mode ?? task.workflow_mode_proposal?.selected_mode ?? "not resolved"}`
3260
3345
  );
3346
+ console.log(` task_tdd_enabled: ${task.tdd_enabled ?? "not frozen"}`);
3347
+ console.log(
3348
+ ` task_tdd_coverage_threshold: ${task.tdd_coverage_threshold ?? "not frozen"}`
3349
+ );
3261
3350
  console.log(` last_agent: ${task.last_agent}`);
3262
3351
  } else {
3263
3352
  console.log(` current_task: ${session.current_task} (task.json missing)`);
@@ -3371,8 +3460,8 @@ async function upgrade(opts) {
3371
3460
  ].filter(Boolean) : [],
3372
3461
  "Will overwrite managed skills, hooks, agents, templates, and generated main-constraint regions.",
3373
3462
  "Will update project-init task to recommend ec-init re-run for version adaptation.",
3374
- "Will migrate legacy confirmation settings to behavior.approval_mode and behavior.workflow_mode.",
3375
- "Will migrate legacy workflow stage metadata; memory content, spec, and project knowledge files remain untouched."
3463
+ "Will migrate behavior config to Approval, Workflow, and default-off Java TDD schema 4 fields.",
3464
+ "Will migrate legacy workflow/TDD task metadata; memory content, spec, and project knowledge files remain untouched."
3376
3465
  ].join("\n");
3377
3466
  if (opts.dryRun) {
3378
3467
  console.log(summary);