@hunyed15/codecgc 0.1.7 → 0.1.9

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.
Files changed (71) hide show
  1. package/.claude/hooks/route-edit.ps1 +58 -57
  2. package/INSTALLATION.md +122 -484
  3. package/README.md +124 -149
  4. package/bin/cgc-external-status.js +4 -0
  5. package/bin/cgc-start.js +4 -0
  6. package/bin/codecgc.js +141 -20
  7. package/codecgc/compound/codecgc-capability-matrix.md +37 -0
  8. package/codecgc/reference/README.md +69 -0
  9. package/codecgc/reference/execution-model.md +3 -1
  10. package/codecgc/reference/maintainer-guide.md +93 -0
  11. package/codecgc/reference/mcp-tool-surface.md +112 -0
  12. package/codecgc/reference/onboarding.md +69 -0
  13. package/codecgc/reference/operation-guide.md +29 -23
  14. package/codecgc/reference/path-contract.md +53 -0
  15. package/codecgc/reference/policy-routing.md +58 -0
  16. package/codecgc/reference/project-structure.md +87 -0
  17. package/codecgc/reference/quickstart.md +110 -0
  18. package/codecgc/reference/real-workflow-loop.md +123 -0
  19. package/codecgc/reference/recovery-loop.md +109 -0
  20. package/codecgc/reference/release-maintenance-playbook.md +4 -1
  21. package/codecgc/reference/troubleshooting.md +114 -0
  22. package/codecgc/roadmap/codecgc-release-maintenance/delivery-plan.md +49 -0
  23. package/codecgc/roadmap/codecgc-release-maintenance/overview.md +41 -0
  24. package/codecgc/roadmap/codecgc-release-maintenance/phases.md +84 -0
  25. package/codecgc/templates/claude/settings.local.json +27 -0
  26. package/codecgc/templates/codex/codecgcrc.json +22 -0
  27. package/codecgc/templates/gemini/codecgc-policy.toml +47 -0
  28. package/codecgcmcp/README.md +57 -11
  29. package/codecgcmcp/src/codecgcmcp/server.py +164 -26
  30. package/codexmcp/src/codexmcp/server.py +45 -0
  31. package/geminimcp/src/geminimcp/server.py +106 -24
  32. package/model-routing.yaml +31 -6
  33. package/package.json +12 -4
  34. package/scripts/audit_codecgc_external_capabilities.py +83 -4
  35. package/scripts/audit_codecgc_historical_audits.py +42 -2
  36. package/scripts/audit_codecgc_package_runtime.py +76 -4
  37. package/scripts/audit_codecgc_release_readiness.py +55 -3
  38. package/scripts/audit_codecgc_workflow_history.py +8 -5
  39. package/scripts/build_codecgc_task.py +69 -45
  40. package/scripts/codecgc_artifact_roots.py +8 -40
  41. package/scripts/codecgc_console_io.py +3 -45
  42. package/scripts/codecgc_executor_registry.py +4 -54
  43. package/scripts/codecgc_path_contract.py +7 -0
  44. package/scripts/codecgc_policy.py +447 -0
  45. package/scripts/codecgc_routing_paths.py +3 -16
  46. package/scripts/codecgc_routing_template.py +11 -135
  47. package/scripts/codecgc_runtime/__init__.py +1 -0
  48. package/scripts/codecgc_runtime/artifacts.py +42 -0
  49. package/scripts/codecgc_runtime/console.py +45 -0
  50. package/scripts/codecgc_runtime/executor_registry.py +55 -0
  51. package/scripts/codecgc_runtime/mcp_config.py +72 -0
  52. package/scripts/codecgc_runtime/path_contract.py +123 -0
  53. package/scripts/codecgc_runtime/paths.py +22 -0
  54. package/scripts/codecgc_runtime/routing_paths.py +16 -0
  55. package/scripts/codecgc_runtime/routing_template.py +171 -0
  56. package/scripts/codecgc_runtime/workflow_runtime.py +72 -0
  57. package/scripts/codecgc_runtime_paths.py +3 -22
  58. package/scripts/codecgc_step_control.py +3 -2
  59. package/scripts/codecgc_workflow_runtime.py +3 -71
  60. package/scripts/entry_codecgc_workflow.py +4 -0
  61. package/scripts/install_codecgc.py +560 -32
  62. package/scripts/normalize_codecgc_audits.py +5 -3
  63. package/scripts/postinstall_codecgc.js +6 -56
  64. package/scripts/review_codecgc_workflow.py +6 -3
  65. package/scripts/route_codecgc_workflow.py +67 -36
  66. package/scripts/run_codecgc_build.py +28 -0
  67. package/scripts/run_codecgc_fix.py +28 -0
  68. package/scripts/run_codecgc_task.py +5 -2
  69. package/scripts/run_codecgc_test.py +28 -0
  70. package/scripts/sync_codecgc_mcp_config.py +4 -54
  71. package/scripts/write_codecgc_review.py +7 -3
@@ -6,6 +6,8 @@ from codecgc_artifact_roots import discover_flow_directory
6
6
  from codecgc_artifact_roots import flow_root
7
7
  from codecgc_artifact_roots import execution_root
8
8
  from codecgc_artifact_roots import normalize_artifact_class
9
+ from codecgc_path_contract import normalize_audit_path_fields
10
+ from codecgc_path_contract import normalize_persisted_project_path
9
11
 
10
12
 
11
13
  WORKSPACE = Path(__file__).resolve().parents[1]
@@ -35,11 +37,11 @@ def build_expected_artifact_file(source: dict[str, Any], artifact_class: str) ->
35
37
  artifact_slug = str(source.get("artifact_slug", "")).strip()
36
38
  if artifact_type not in {"feature", "issue"} or not artifact_slug:
37
39
  current = str(source.get("artifact_file", "")).strip()
38
- return replace_repo_name(current) if current else current
40
+ return normalize_persisted_project_path(replace_repo_name(current)) if current else current
39
41
 
40
42
  base_slug = artifact_slug[11:] if len(artifact_slug) > 11 and artifact_slug[4] == "-" else artifact_slug
41
43
  checklist_name = f"{base_slug}-checklist.yaml" if artifact_type == "feature" else f"{base_slug}-fix.yaml"
42
- return str(flow_root(artifact_type, artifact_class) / artifact_slug / checklist_name)
44
+ return normalize_persisted_project_path(flow_root(artifact_type, artifact_class) / artifact_slug / checklist_name)
43
45
 
44
46
 
45
47
  def normalize_audit_record(data: dict[str, Any]) -> tuple[dict[str, Any], str]:
@@ -55,7 +57,7 @@ def normalize_audit_record(data: dict[str, Any]) -> tuple[dict[str, Any], str]:
55
57
  source["artifact_file"] = build_expected_artifact_file(source, artifact_class)
56
58
  source["artifact_class"] = artifact_class
57
59
  data["source"] = source
58
- return data, artifact_class
60
+ return normalize_audit_path_fields(data), artifact_class
59
61
 
60
62
 
61
63
  def normalize_file(path: Path) -> tuple[Path, bool]:
@@ -1,9 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { spawnSync } = require("node:child_process");
4
- const path = require("node:path");
5
-
6
- function shouldRunUserInstall() {
3
+ function isGlobalInstall() {
7
4
  const globalFlag = String(process.env.npm_config_global || "").toLowerCase();
8
5
  const localPrefix = String(process.env.npm_config_local_prefix || "");
9
6
  const prefix = String(process.env.npm_config_prefix || "");
@@ -11,64 +8,17 @@ function shouldRunUserInstall() {
11
8
  if (globalFlag === "true") {
12
9
  return true;
13
10
  }
14
- if (prefix && localPrefix && prefix !== localPrefix) {
15
- return true;
16
- }
17
- return false;
18
- }
19
-
20
- function findPython() {
21
- const override = String(process.env.CODECGC_PYTHON_COMMAND || "").trim();
22
- const candidates = override
23
- ? [override]
24
- : (process.platform === "win32" ? ["python", "py"] : ["python3", "python"]);
25
-
26
- for (const command of candidates) {
27
- const probe = spawnSync(command, ["--version"], {
28
- encoding: "utf8",
29
- shell: false,
30
- });
31
- if (probe.status === 0) {
32
- return command;
33
- }
34
- }
35
- return "";
11
+ return Boolean(prefix && localPrefix && prefix !== localPrefix);
36
12
  }
37
13
 
38
14
  function main() {
39
- if (!shouldRunUserInstall()) {
15
+ if (!isGlobalInstall()) {
40
16
  return 0;
41
17
  }
42
18
 
43
- const python = findPython();
44
- if (!python) {
45
- console.warn("[codecgc] Skipped automatic Claude integration: Python was not found.");
46
- console.warn("[codecgc] Run `cgc-install --mode user` after installing Python.");
47
- return 0;
48
- }
49
-
50
- const repoRoot = path.resolve(__dirname, "..");
51
- const installScript = path.join(repoRoot, "scripts", "install_codecgc.py");
52
- const result = spawnSync(
53
- python,
54
- [installScript, "--mode", "user", "--format", "summary"],
55
- {
56
- cwd: repoRoot,
57
- stdio: "inherit",
58
- shell: false,
59
- env: {
60
- ...process.env,
61
- PYTHONIOENCODING: process.env.PYTHONIOENCODING || "utf-8",
62
- PYTHONUTF8: process.env.PYTHONUTF8 || "1",
63
- },
64
- },
65
- );
66
-
67
- if (result.status !== 0) {
68
- console.warn("[codecgc] Automatic Claude integration did not complete during npm install.");
69
- console.warn("[codecgc] You can retry manually with `cgc-install --mode user`.");
70
- }
71
-
19
+ console.warn("[codecgc] Global CLI installed.");
20
+ console.warn("[codecgc] CodeCGC no longer writes Claude user-level files during npm install.");
21
+ console.warn("[codecgc] Run `cgc-install` from each target project to create project-local .mcp.json, .claude/, and model-routing.yaml.");
72
22
  return 0;
73
23
  }
74
24
 
@@ -4,6 +4,8 @@ from pathlib import Path
4
4
 
5
5
  from codecgc_console_io import configure_utf8_stdio
6
6
  from codecgc_console_io import print_json
7
+ from codecgc_path_contract import normalize_persisted_project_path
8
+ from codecgc_path_contract import resolve_project_path
7
9
  from codecgc_review_control import evaluate_review
8
10
  from write_codecgc_review import load_json
9
11
  from write_codecgc_review import render_review_decision
@@ -33,10 +35,11 @@ def main() -> int:
33
35
  args = parser.parse_args()
34
36
 
35
37
  try:
36
- audit = load_json(Path(args.audit_file))
38
+ audit_file = resolve_project_path(args.audit_file)
39
+ audit = load_json(audit_file)
37
40
  values = evaluate_review(audit, args.decision, args.risk, args.next_step)
38
41
  writeback = write_review(
39
- audit_path=Path(args.audit_file),
42
+ audit_path=audit_file,
40
43
  decision=args.decision,
41
44
  risks=args.risk,
42
45
  next_step=args.next_step,
@@ -44,7 +47,7 @@ def main() -> int:
44
47
  )
45
48
  result = {
46
49
  "success": True,
47
- "audit_file": args.audit_file,
50
+ "audit_file": normalize_persisted_project_path(audit_file),
48
51
  "requested_decision": args.decision,
49
52
  "final_decision": values["final_decision"],
50
53
  "accepted": values["accepted"],
@@ -13,6 +13,7 @@ from codecgc_artifact_roots import normalize_artifact_class
13
13
  from codecgc_command_surface import to_public_command
14
14
  from codecgc_console_io import configure_utf8_stdio
15
15
  from codecgc_console_io import print_json
16
+ from codecgc_path_contract import normalize_persisted_project_path
16
17
  from codecgc_routing_paths import resolve_active_routing_file
17
18
  from codecgc_runtime_paths import PACKAGE_ROOT
18
19
  from codecgc_step_control import is_test_codecgc_block
@@ -23,6 +24,10 @@ WORKSPACE = PACKAGE_ROOT
23
24
  ROUTING_FILE = resolve_active_routing_file()
24
25
 
25
26
 
27
+ def display_path(path: Path | None) -> str:
28
+ return normalize_persisted_project_path(path) if path else ""
29
+
30
+
26
31
  def attach_route_summary(result: dict[str, Any]) -> dict[str, Any]:
27
32
  review = result.get("review", {}) if isinstance(result.get("review"), dict) else {}
28
33
  current_step = result.get("current_step", {}) if isinstance(result.get("current_step"), dict) else {}
@@ -189,16 +194,28 @@ def find_audit_for_task_id(task_id: str, artifact_class: str) -> tuple[Path | No
189
194
  return audit_path, audit
190
195
 
191
196
 
192
- def extract_review_metadata(path: Path) -> dict[str, Any]:
193
- text = load_text(path)
197
+ def parse_review_metadata(markdown: str) -> dict[str, Any]:
198
+ text = str(markdown or "")
194
199
  decision_match = re.search(
195
200
  r"## [45]\. (?:Review Decision|审核结论)\s+[\r\n]+- (?:审核结果:\s*)?(accepted|changes-requested|通过|需修改)",
196
201
  text,
197
202
  )
198
203
  if not decision_match:
199
204
  decision_match = re.search(r"- (?:Final decision|最终决策): (accepted|changes-requested|通过|需修改)", text)
205
+ if not decision_match:
206
+ decision_match = re.search(
207
+ r"-\s*(?:Review decision|审核决策|审核结果|瀹℃牳鍐崇瓥):\s*(accepted|changes-requested|通过|需修改)",
208
+ text,
209
+ flags=re.IGNORECASE,
210
+ )
200
211
  task_match = re.search(r"- (?:Reviewed task_id|审核 task_id): (.+)", text)
201
212
  step_match = re.search(r"- (?:Reviewed step_number|审核 step_number|审核步骤序号): (\d+)", text)
213
+ if not step_match:
214
+ step_match = re.search(
215
+ r"-\s*(?:Reviewed step_number|审核 step_number|审核步骤序号|瀹℃牳姝ラ搴忓彿):\s*(\d+)",
216
+ text,
217
+ flags=re.IGNORECASE,
218
+ )
202
219
  action_kind_match = re.search(r"- (?:Review action kind|审核动作类型): (.+)", text)
203
220
  fallback_stage_match = re.search(r"- (?:Review fallback stage|审核回退阶段): (.+)", text)
204
221
  policy_reason_match = re.search(r"- (?:Review policy reason|审核策略原因): (.+)", text)
@@ -214,6 +231,10 @@ def extract_review_metadata(path: Path) -> dict[str, Any]:
214
231
  }
215
232
 
216
233
 
234
+ def extract_review_metadata(path: Path) -> dict[str, Any]:
235
+ return parse_review_metadata(load_text(path))
236
+
237
+
217
238
  def review_matches_step(review: dict[str, Any], step: dict[str, Any]) -> bool:
218
239
  return (
219
240
  str(review.get("task_id", "")) == str(step.get("task_id", ""))
@@ -265,7 +286,7 @@ def route_feature(slug: str) -> dict[str, Any]:
265
286
  "flow": "feature",
266
287
  "slug": slug,
267
288
  "artifact_class": artifact_class,
268
- "directory": str(directory),
289
+ "directory": display_path(directory),
269
290
  "recommended_command": "cgc-plan",
270
291
  "reason": "当前功能开发工作流骨架不完整。",
271
292
  "next": "补齐或修复缺失的功能开发产物文件。",
@@ -277,7 +298,7 @@ def route_feature(slug: str) -> dict[str, Any]:
277
298
  "flow": "feature",
278
299
  "slug": slug,
279
300
  "artifact_class": artifact_class,
280
- "directory": str(directory),
301
+ "directory": display_path(directory),
281
302
  "recommended_command": "cgc-plan",
282
303
  "reason": "当前功能开发工作流已存在,但还不可执行。",
283
304
  "next": "继续细化设计,并补上有效的 CodeCGC 步骤契约。",
@@ -289,7 +310,7 @@ def route_feature(slug: str) -> dict[str, Any]:
289
310
  "flow": "feature",
290
311
  "slug": slug,
291
312
  "artifact_class": artifact_class,
292
- "directory": str(directory),
313
+ "directory": display_path(directory),
293
314
  "recommended_command": "cgc-plan",
294
315
  "reason": "当前功能开发清单里仍然存在仅规划用途的拆分或路由确认步骤。",
295
316
  "next": "先完成必须的拆分或路由澄清,再执行剩余的限定范围步骤。",
@@ -302,7 +323,7 @@ def route_feature(slug: str) -> dict[str, Any]:
302
323
  "flow": "feature",
303
324
  "slug": slug,
304
325
  "artifact_class": artifact_class,
305
- "directory": str(directory),
326
+ "directory": display_path(directory),
306
327
  "recommended_command": "cgc-plan",
307
328
  "reason": "当前功能开发清单仍以不可执行的占位步骤开头。",
308
329
  "next": "先澄清范围、归属和目标路径,再尝试执行该功能开发工作流。",
@@ -335,7 +356,7 @@ def route_feature(slug: str) -> dict[str, Any]:
335
356
  "flow": "feature",
336
357
  "slug": slug,
337
358
  "artifact_class": artifact_class,
338
- "directory": str(directory),
359
+ "directory": display_path(directory),
339
360
  "review": review,
340
361
  "current_step": None,
341
362
  "audit_path": "",
@@ -348,7 +369,7 @@ def route_feature(slug: str) -> dict[str, Any]:
348
369
  "flow": "feature",
349
370
  "slug": slug,
350
371
  "artifact_class": artifact_class,
351
- "directory": str(directory),
372
+ "directory": display_path(directory),
352
373
  "review": review,
353
374
  "current_step": None,
354
375
  "audit_path": "",
@@ -363,6 +384,7 @@ def route_feature(slug: str) -> dict[str, Any]:
363
384
  "step_number": int(next_step.get("step_number", 0) or 0),
364
385
  "task_id": str(next_step.get("task_id", "")),
365
386
  "kind": str(next_step.get("kind", "")),
387
+ "step_type": str(next_step.get("step_type", "")),
366
388
  "target_paths": next_step.get("target_paths", []),
367
389
  "task_summary": str(next_step.get("task_summary", "")),
368
390
  }
@@ -372,7 +394,7 @@ def route_feature(slug: str) -> dict[str, Any]:
372
394
  "task_id": current_step["task_id"],
373
395
  "task_summary": current_step["task_summary"],
374
396
  "target_paths": current_step["target_paths"],
375
- "step_type": "test" if "-test-step-" in current_step["task_id"] else "",
397
+ "step_type": current_step["step_type"] or ("test" if "-test-step-" in current_step["task_id"] else ""),
376
398
  }
377
399
  ):
378
400
  return {
@@ -380,25 +402,29 @@ def route_feature(slug: str) -> dict[str, Any]:
380
402
  "flow": "feature",
381
403
  "slug": slug,
382
404
  "artifact_class": artifact_class,
383
- "directory": str(directory),
405
+ "directory": display_path(directory),
384
406
  "review": review,
385
407
  "current_step": current_step,
386
- "audit_path": str(audit_path) if audit_path else "",
408
+ "audit_path": display_path(audit_path),
387
409
  "recommended_command": "cgc-test",
388
410
  "reason": "当前功能开发工作流正在等待测试步骤执行。",
389
411
  "next": "执行当前测试步骤。",
390
412
  }
391
413
 
392
- if review_for_current_step and review.get("decision") == "changes-requested":
414
+ if (
415
+ review_for_current_step
416
+ and review.get("decision") == "changes-requested"
417
+ and not (audit and audit_is_ready_for_review(audit, next_step))
418
+ ):
393
419
  return {
394
420
  "success": True,
395
421
  "flow": "feature",
396
422
  "slug": slug,
397
423
  "artifact_class": artifact_class,
398
- "directory": str(directory),
424
+ "directory": display_path(directory),
399
425
  "review": review,
400
426
  "current_step": current_step,
401
- "audit_path": str(audit_path) if audit_path else "",
427
+ "audit_path": display_path(audit_path),
402
428
  "recommended_command": "cgc-build",
403
429
  "reason": "当前功能开发步骤在审核后仍需继续补充修改。",
404
430
  "next": "完成要求的后续修改后,重新执行当前功能开发步骤。",
@@ -411,10 +437,10 @@ def route_feature(slug: str) -> dict[str, Any]:
411
437
  "flow": "feature",
412
438
  "slug": slug,
413
439
  "artifact_class": artifact_class,
414
- "directory": str(directory),
440
+ "directory": display_path(directory),
415
441
  "review": review,
416
442
  "current_step": current_step,
417
- "audit_path": str(audit_path) if audit_path else "",
443
+ "audit_path": display_path(audit_path),
418
444
  "recommended_command": "cgc-review",
419
445
  "reason": "当前功能开发步骤已有执行证据,但还没有对应的审核结论。",
420
446
  "next": f"审核最新审计产物 {audit_path},并回写验收结论。",
@@ -424,10 +450,10 @@ def route_feature(slug: str) -> dict[str, Any]:
424
450
  "flow": "feature",
425
451
  "slug": slug,
426
452
  "artifact_class": artifact_class,
427
- "directory": str(directory),
453
+ "directory": display_path(directory),
428
454
  "review": review,
429
455
  "current_step": current_step,
430
- "audit_path": str(audit_path) if audit_path else "",
456
+ "audit_path": display_path(audit_path),
431
457
  "recommended_command": "cgc-build",
432
458
  "reason": "当前功能开发步骤还没有“通过”审核结论。",
433
459
  "next": "执行或继续推进当前待执行的功能开发步骤。",
@@ -438,7 +464,7 @@ def route_feature(slug: str) -> dict[str, Any]:
438
464
  "flow": "feature",
439
465
  "slug": slug,
440
466
  "artifact_class": artifact_class,
441
- "directory": str(directory),
467
+ "directory": display_path(directory),
442
468
  "review": review,
443
469
  "current_step": current_step,
444
470
  "audit_path": "",
@@ -472,7 +498,7 @@ def route_issue(slug: str) -> dict[str, Any]:
472
498
  "flow": "issue",
473
499
  "slug": slug,
474
500
  "artifact_class": artifact_class,
475
- "directory": str(directory),
501
+ "directory": display_path(directory),
476
502
  "recommended_command": "cgc-plan",
477
503
  "reason": "Issue 工作流骨架不完整。",
478
504
  "next": "补齐或修复缺失的 issue 产物文件。",
@@ -484,7 +510,7 @@ def route_issue(slug: str) -> dict[str, Any]:
484
510
  "flow": "issue",
485
511
  "slug": slug,
486
512
  "artifact_class": artifact_class,
487
- "directory": str(directory),
513
+ "directory": display_path(directory),
488
514
  "recommended_command": "cgc-plan",
489
515
  "reason": "当前问题修复工作流已存在,但还不可执行。",
490
516
  "next": "继续细化修复范围,并补上有效的 CodeCGC 步骤契约。",
@@ -496,7 +522,7 @@ def route_issue(slug: str) -> dict[str, Any]:
496
522
  "flow": "issue",
497
523
  "slug": slug,
498
524
  "artifact_class": artifact_class,
499
- "directory": str(directory),
525
+ "directory": display_path(directory),
500
526
  "recommended_command": "cgc-plan",
501
527
  "reason": "当前问题修复清单里仍然存在仅规划用途的拆分或路由确认步骤。",
502
528
  "next": "先完成必须的拆分或路由澄清,再执行剩余的限定范围修复步骤。",
@@ -509,7 +535,7 @@ def route_issue(slug: str) -> dict[str, Any]:
509
535
  "flow": "issue",
510
536
  "slug": slug,
511
537
  "artifact_class": artifact_class,
512
- "directory": str(directory),
538
+ "directory": display_path(directory),
513
539
  "recommended_command": "cgc-plan",
514
540
  "reason": "当前问题修复清单仍以不可执行的占位步骤开头。",
515
541
  "next": "先澄清修复范围、归属和目标路径,再尝试执行该问题修复工作流。",
@@ -542,7 +568,7 @@ def route_issue(slug: str) -> dict[str, Any]:
542
568
  "flow": "issue",
543
569
  "slug": slug,
544
570
  "artifact_class": artifact_class,
545
- "directory": str(directory),
571
+ "directory": display_path(directory),
546
572
  "review": review,
547
573
  "current_step": None,
548
574
  "audit_path": "",
@@ -555,7 +581,7 @@ def route_issue(slug: str) -> dict[str, Any]:
555
581
  "flow": "issue",
556
582
  "slug": slug,
557
583
  "artifact_class": artifact_class,
558
- "directory": str(directory),
584
+ "directory": display_path(directory),
559
585
  "review": review,
560
586
  "current_step": None,
561
587
  "audit_path": "",
@@ -570,6 +596,7 @@ def route_issue(slug: str) -> dict[str, Any]:
570
596
  "step_number": int(next_step.get("step_number", 0) or 0),
571
597
  "task_id": str(next_step.get("task_id", "")),
572
598
  "kind": str(next_step.get("kind", "")),
599
+ "step_type": str(next_step.get("step_type", "")),
573
600
  "target_paths": next_step.get("target_paths", []),
574
601
  "task_summary": str(next_step.get("task_summary", "")),
575
602
  }
@@ -579,7 +606,7 @@ def route_issue(slug: str) -> dict[str, Any]:
579
606
  "task_id": current_step["task_id"],
580
607
  "task_summary": current_step["task_summary"],
581
608
  "target_paths": current_step["target_paths"],
582
- "step_type": "test" if "-test-step-" in current_step["task_id"] else "",
609
+ "step_type": current_step["step_type"] or ("test" if "-test-step-" in current_step["task_id"] else ""),
583
610
  }
584
611
  ):
585
612
  return {
@@ -587,25 +614,29 @@ def route_issue(slug: str) -> dict[str, Any]:
587
614
  "flow": "issue",
588
615
  "slug": slug,
589
616
  "artifact_class": artifact_class,
590
- "directory": str(directory),
617
+ "directory": display_path(directory),
591
618
  "review": review,
592
619
  "current_step": current_step,
593
- "audit_path": str(audit_path) if audit_path else "",
620
+ "audit_path": display_path(audit_path),
594
621
  "recommended_command": "cgc-test",
595
622
  "reason": "当前问题修复工作流正在等待测试步骤执行。",
596
623
  "next": "执行当前测试步骤。",
597
624
  }
598
625
 
599
- if review_for_current_step and review.get("decision") == "changes-requested":
626
+ if (
627
+ review_for_current_step
628
+ and review.get("decision") == "changes-requested"
629
+ and not (audit and audit_is_ready_for_review(audit, next_step))
630
+ ):
600
631
  return {
601
632
  "success": True,
602
633
  "flow": "issue",
603
634
  "slug": slug,
604
635
  "artifact_class": artifact_class,
605
- "directory": str(directory),
636
+ "directory": display_path(directory),
606
637
  "review": review,
607
638
  "current_step": current_step,
608
- "audit_path": str(audit_path) if audit_path else "",
639
+ "audit_path": display_path(audit_path),
609
640
  "recommended_command": "cgc-fix",
610
641
  "reason": "当前问题修复步骤在审核后仍需继续补充修改。",
611
642
  "next": "完成要求的后续修复后,重新执行当前问题修复步骤。",
@@ -618,10 +649,10 @@ def route_issue(slug: str) -> dict[str, Any]:
618
649
  "flow": "issue",
619
650
  "slug": slug,
620
651
  "artifact_class": artifact_class,
621
- "directory": str(directory),
652
+ "directory": display_path(directory),
622
653
  "review": review,
623
654
  "current_step": current_step,
624
- "audit_path": str(audit_path) if audit_path else "",
655
+ "audit_path": display_path(audit_path),
625
656
  "recommended_command": "cgc-review",
626
657
  "reason": "当前问题修复步骤已有执行证据,但还没有对应的审核结论。",
627
658
  "next": f"审核最新审计产物 {audit_path},并回写修复结论。",
@@ -631,10 +662,10 @@ def route_issue(slug: str) -> dict[str, Any]:
631
662
  "flow": "issue",
632
663
  "slug": slug,
633
664
  "artifact_class": artifact_class,
634
- "directory": str(directory),
665
+ "directory": display_path(directory),
635
666
  "review": review,
636
667
  "current_step": current_step,
637
- "audit_path": str(audit_path) if audit_path else "",
668
+ "audit_path": display_path(audit_path),
638
669
  "recommended_command": "cgc-fix",
639
670
  "reason": "当前问题修复步骤还没有“通过”审核结论。",
640
671
  "next": "执行或继续推进当前待执行的问题修复步骤。",
@@ -645,7 +676,7 @@ def route_issue(slug: str) -> dict[str, Any]:
645
676
  "flow": "issue",
646
677
  "slug": slug,
647
678
  "artifact_class": artifact_class,
648
- "directory": str(directory),
679
+ "directory": display_path(directory),
649
680
  "review": review,
650
681
  "current_step": current_step,
651
682
  "audit_path": "",
@@ -7,6 +7,9 @@ from codecgc_console_io import configure_utf8_stdio
7
7
  from codecgc_console_io import print_json
8
8
  from codecgc_flow_control import build_execution_result
9
9
  from codecgc_flow_control import build_not_ready_result
10
+ from codecgc_policy import load_policy
11
+ from codecgc_policy import validate_executor_target
12
+ from codecgc_routing_paths import resolve_active_routing_file
10
13
  from codecgc_session_recovery import resolve_session_id_from_task
11
14
  from codecgc_step_control import get_step_metadata
12
15
  from codecgc_step_control import select_next_executable_step
@@ -62,6 +65,31 @@ def main() -> int:
62
65
  print_json(result)
63
66
  return 1
64
67
 
68
+ try:
69
+ policy_result = validate_executor_target(
70
+ str(selected.get("kind", "")),
71
+ [str(path) for path in selected.get("target_paths", [])],
72
+ load_policy(resolve_active_routing_file()),
73
+ )
74
+ if not policy_result.get("allowed"):
75
+ result = {
76
+ "success": False,
77
+ "flow": "feature",
78
+ "slug": args.slug,
79
+ "state": "not-ready",
80
+ "failure_type": "policy",
81
+ "route": route,
82
+ "selected_step": selected,
83
+ "policy": policy_result,
84
+ "recommended_command": to_public_command("cgc-plan"),
85
+ "next": "Adjust target_paths or split the task before build execution.",
86
+ }
87
+ print_json(result)
88
+ return 1
89
+ except Exception as error:
90
+ print_json({"success": False, "error": str(error), "failure_type": "policy"}, file=sys.stderr)
91
+ return 1
92
+
65
93
  effective_session_id = args.session_id.strip()
66
94
  if not effective_session_id:
67
95
  effective_session_id = resolve_session_id_from_task(
@@ -7,6 +7,9 @@ from codecgc_console_io import configure_utf8_stdio
7
7
  from codecgc_console_io import print_json
8
8
  from codecgc_flow_control import build_execution_result
9
9
  from codecgc_flow_control import build_not_ready_result
10
+ from codecgc_policy import load_policy
11
+ from codecgc_policy import validate_executor_target
12
+ from codecgc_routing_paths import resolve_active_routing_file
10
13
  from codecgc_session_recovery import resolve_session_id_from_task
11
14
  from codecgc_step_control import get_step_metadata
12
15
  from codecgc_step_control import select_next_executable_step
@@ -62,6 +65,31 @@ def main() -> int:
62
65
  print_json(result)
63
66
  return 1
64
67
 
68
+ try:
69
+ policy_result = validate_executor_target(
70
+ str(selected.get("kind", "")),
71
+ [str(path) for path in selected.get("target_paths", [])],
72
+ load_policy(resolve_active_routing_file()),
73
+ )
74
+ if not policy_result.get("allowed"):
75
+ result = {
76
+ "success": False,
77
+ "flow": "issue",
78
+ "slug": args.slug,
79
+ "state": "not-ready",
80
+ "failure_type": "policy",
81
+ "route": route,
82
+ "selected_step": selected,
83
+ "policy": policy_result,
84
+ "recommended_command": to_public_command("cgc-plan"),
85
+ "next": "Adjust target_paths or split the task before fix execution.",
86
+ }
87
+ print_json(result)
88
+ return 1
89
+ except Exception as error:
90
+ print_json({"success": False, "error": str(error), "failure_type": "policy"}, file=sys.stderr)
91
+ return 1
92
+
65
93
  effective_session_id = args.session_id.strip()
66
94
  if not effective_session_id:
67
95
  effective_session_id = resolve_session_id_from_task(
@@ -14,6 +14,8 @@ from codecgc_executor_registry import build_executor_env
14
14
  from codecgc_executor_registry import get_executor_config
15
15
  from codecgc_file_evidence import snapshot_workspace
16
16
  from codecgc_file_evidence import verify_workspace_changes
17
+ from codecgc_path_contract import normalize_audit_path_fields
18
+ from codecgc_path_contract import normalize_persisted_project_path
17
19
  from codecgc_runtime_paths import PACKAGE_ROOT
18
20
  from codecgc_runtime_paths import PROJECT_ROOT
19
21
  from mcp import ClientSession
@@ -215,7 +217,7 @@ def build_audit_record(
215
217
  if not isinstance(reported_changed_files, list):
216
218
  reported_changed_files = []
217
219
 
218
- return {
220
+ record = {
219
221
  "product": "CodeCGC",
220
222
  "version": 1,
221
223
  "mode": mode,
@@ -262,6 +264,7 @@ def build_audit_record(
262
264
  },
263
265
  },
264
266
  }
267
+ return normalize_audit_path_fields(record, PROJECT_WORKSPACE)
265
268
 
266
269
 
267
270
  def write_audit_file(audit_root: Path, audit_record: dict[str, Any]) -> Path:
@@ -379,7 +382,7 @@ async def run_task(args: argparse.Namespace) -> dict[str, Any]:
379
382
  "result": task_result,
380
383
  "audit": {
381
384
  "written": not args.skip_audit_write,
382
- "path": str(audit_path) if audit_path else "",
385
+ "path": normalize_persisted_project_path(audit_path) if audit_path else "",
383
386
  },
384
387
  }
385
388
  return sanitize_for_json(output)
@@ -7,6 +7,9 @@ from codecgc_console_io import configure_utf8_stdio
7
7
  from codecgc_console_io import print_json
8
8
  from codecgc_flow_control import build_execution_result
9
9
  from codecgc_flow_control import build_not_ready_result
10
+ from codecgc_policy import load_policy
11
+ from codecgc_policy import validate_executor_target
12
+ from codecgc_routing_paths import resolve_active_routing_file
10
13
  from codecgc_session_recovery import resolve_session_id_from_task
11
14
  from codecgc_step_control import get_step_metadata
12
15
  from codecgc_step_control import select_next_executable_step
@@ -63,6 +66,31 @@ def main() -> int:
63
66
  print_json(result)
64
67
  return 1
65
68
 
69
+ try:
70
+ policy_result = validate_executor_target(
71
+ str(selected.get("kind", "")),
72
+ [str(path) for path in selected.get("target_paths", [])],
73
+ load_policy(resolve_active_routing_file()),
74
+ )
75
+ if not policy_result.get("allowed"):
76
+ result = {
77
+ "success": False,
78
+ "flow": args.flow,
79
+ "slug": args.slug,
80
+ "state": "not-ready",
81
+ "failure_type": "policy",
82
+ "route": route,
83
+ "selected_step": selected,
84
+ "policy": policy_result,
85
+ "recommended_command": to_public_command("cgc-plan"),
86
+ "next": "Adjust target_paths or split the task before test execution.",
87
+ }
88
+ print_json(result)
89
+ return 1
90
+ except Exception as error:
91
+ print_json({"success": False, "error": str(error), "failure_type": "policy"}, file=sys.stderr)
92
+ return 1
93
+
66
94
  effective_session_id = args.session_id.strip()
67
95
  if not effective_session_id:
68
96
  effective_session_id = resolve_session_id_from_task(