super-engineer-workflow 0.1.5 → 0.1.6
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
package/package.json
CHANGED
package/scripts/se-e2e-test.py
CHANGED
|
@@ -24,6 +24,7 @@ def main() -> None:
|
|
|
24
24
|
os.environ["USERPROFILE"] = str(home)
|
|
25
25
|
test_templates_cli(root)
|
|
26
26
|
test_openspec_state_and_bridge(root)
|
|
27
|
+
test_incomplete_plan_session_is_reused(root)
|
|
27
28
|
test_todo_auto_session_and_verify_compaction(root)
|
|
28
29
|
print("e2e_test=ok")
|
|
29
30
|
|
|
@@ -323,6 +324,84 @@ def test_todo_auto_session_and_verify_compaction(root: Path) -> None:
|
|
|
323
324
|
raise AssertionError("notification should be marked skipped when no provider is configured")
|
|
324
325
|
|
|
325
326
|
|
|
327
|
+
def test_incomplete_plan_session_is_reused(root: Path) -> None:
|
|
328
|
+
workspace = root / "incomplete-plan-workspace"
|
|
329
|
+
code = root / "broken-code" / "not-a-project"
|
|
330
|
+
demand_dir = workspace / "superengineer" / "10-broken"
|
|
331
|
+
demand_dir.mkdir(parents=True)
|
|
332
|
+
code.mkdir(parents=True)
|
|
333
|
+
(workspace / "docs").mkdir(parents=True)
|
|
334
|
+
(workspace / "workspace.yml").write_text(
|
|
335
|
+
"\n".join(
|
|
336
|
+
[
|
|
337
|
+
"version: 1",
|
|
338
|
+
"mode: auto",
|
|
339
|
+
"workflow_source: todo",
|
|
340
|
+
"vars:",
|
|
341
|
+
" demand_name: 10-broken",
|
|
342
|
+
"todo_file: superengineer/${demand_name}/todo.md",
|
|
343
|
+
"reference_files: []",
|
|
344
|
+
"code_path: ../broken-code/not-a-project",
|
|
345
|
+
"output_dir: superengineer/${demand_name}/output",
|
|
346
|
+
"",
|
|
347
|
+
]
|
|
348
|
+
),
|
|
349
|
+
encoding="utf-8",
|
|
350
|
+
)
|
|
351
|
+
(demand_dir / "todo.md").write_text(
|
|
352
|
+
"# 限制条件\n"
|
|
353
|
+
"- 修改的服务是 broken-service\n\n"
|
|
354
|
+
"# 待办事项\n\n"
|
|
355
|
+
"- [ ] 增加一个测试接口\n",
|
|
356
|
+
encoding="utf-8",
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
first_apply = run(
|
|
360
|
+
[
|
|
361
|
+
sys.executable,
|
|
362
|
+
str(RUN_WORKFLOW),
|
|
363
|
+
"route-se",
|
|
364
|
+
"--workspace",
|
|
365
|
+
str(workspace),
|
|
366
|
+
"--command-text",
|
|
367
|
+
"/se:apply",
|
|
368
|
+
],
|
|
369
|
+
check=False,
|
|
370
|
+
)
|
|
371
|
+
if first_apply.returncode == 0 or "未找到可识别的项目目录" not in first_apply.output:
|
|
372
|
+
raise AssertionError("first /se:apply should fail before plan is generated")
|
|
373
|
+
current = read_json(workspace / ".super-engineer" / "current-session.json")
|
|
374
|
+
first_session_id = current["session_id"]
|
|
375
|
+
sessions_dir = workspace / ".super-engineer" / "sessions"
|
|
376
|
+
output_dir = workspace / "superengineer" / "10-broken" / "output"
|
|
377
|
+
if len(list(sessions_dir.iterdir())) != 1:
|
|
378
|
+
raise AssertionError("first failed /se:apply should create exactly one reusable data session")
|
|
379
|
+
if output_dir.exists() and list(output_dir.iterdir()):
|
|
380
|
+
raise AssertionError("failed plan should not create an empty output report session")
|
|
381
|
+
|
|
382
|
+
second_apply = run(
|
|
383
|
+
[
|
|
384
|
+
sys.executable,
|
|
385
|
+
str(RUN_WORKFLOW),
|
|
386
|
+
"route-se",
|
|
387
|
+
"--workspace",
|
|
388
|
+
str(workspace),
|
|
389
|
+
"--command-text",
|
|
390
|
+
"/se:apply",
|
|
391
|
+
],
|
|
392
|
+
check=False,
|
|
393
|
+
)
|
|
394
|
+
if second_apply.returncode == 0 or "session_action=reused_incomplete" not in second_apply.output:
|
|
395
|
+
raise AssertionError("second /se:apply should reuse the incomplete planning session")
|
|
396
|
+
current = read_json(workspace / ".super-engineer" / "current-session.json")
|
|
397
|
+
if current["session_id"] != first_session_id:
|
|
398
|
+
raise AssertionError("incomplete planning session should remain current")
|
|
399
|
+
if len(list(sessions_dir.iterdir())) != 1:
|
|
400
|
+
raise AssertionError("repeated failed /se:apply should not create another data session")
|
|
401
|
+
if output_dir.exists() and list(output_dir.iterdir()):
|
|
402
|
+
raise AssertionError("repeated failed /se:apply should not create output report sessions")
|
|
403
|
+
|
|
404
|
+
|
|
326
405
|
def run(command: list[str], check: bool = True, env: dict[str, str] | None = None):
|
|
327
406
|
result = subprocess.run(
|
|
328
407
|
command,
|
|
@@ -652,17 +652,13 @@ def create_session(config: dict[str, Any]) -> dict[str, Any]:
|
|
|
652
652
|
},
|
|
653
653
|
)
|
|
654
654
|
Path(session_meta["data_dir"]).mkdir(parents=True, exist_ok=True)
|
|
655
|
-
Path(session_meta["report_dir"]).mkdir(parents=True, exist_ok=True)
|
|
656
655
|
write_managed_json(config, current_session_file(config), session_meta)
|
|
657
656
|
return session_meta
|
|
658
657
|
|
|
659
658
|
|
|
660
659
|
def current_session_meta(config: dict[str, Any]) -> dict[str, Any]:
|
|
661
660
|
session_meta = read_json(current_session_file(config), {})
|
|
662
|
-
|
|
663
|
-
Path(normalized["data_dir"]).mkdir(parents=True, exist_ok=True)
|
|
664
|
-
Path(normalized["report_dir"]).mkdir(parents=True, exist_ok=True)
|
|
665
|
-
return normalized
|
|
661
|
+
return _normalize_session_meta(config, session_meta)
|
|
666
662
|
|
|
667
663
|
|
|
668
664
|
def current_session_status(config: dict[str, Any], session_meta: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
@@ -678,16 +674,26 @@ def active_session_for_plan(config: dict[str, Any]) -> dict[str, Any] | None:
|
|
|
678
674
|
session_meta = current_session_meta(config)
|
|
679
675
|
except FileNotFoundError:
|
|
680
676
|
return None
|
|
681
|
-
|
|
682
|
-
|
|
677
|
+
data_dir = Path(str(session_meta["data_dir"]))
|
|
678
|
+
has_plan = data_artifact_path(config, "plan.json", session_meta).exists()
|
|
683
679
|
status = current_session_status(config, session_meta)
|
|
684
680
|
status_phase = str(status.get("phase", "") or "").strip()
|
|
685
681
|
if status_phase in ("done", "archived", "blocked"):
|
|
686
682
|
return None
|
|
683
|
+
if not has_plan and data_dir.exists():
|
|
684
|
+
return {
|
|
685
|
+
"session": session_meta,
|
|
686
|
+
"status": status,
|
|
687
|
+
"phase": status_phase or "draft",
|
|
688
|
+
"incomplete": True,
|
|
689
|
+
}
|
|
690
|
+
if not has_plan:
|
|
691
|
+
return None
|
|
687
692
|
return {
|
|
688
693
|
"session": session_meta,
|
|
689
694
|
"status": status,
|
|
690
695
|
"phase": status_phase or "plan",
|
|
696
|
+
"incomplete": False,
|
|
691
697
|
}
|
|
692
698
|
|
|
693
699
|
|
|
@@ -697,6 +703,8 @@ def ensure_plan_can_run(config: dict[str, Any]) -> dict[str, Any] | None:
|
|
|
697
703
|
return None
|
|
698
704
|
phase = str(active.get("phase", "")).strip()
|
|
699
705
|
session = active.get("session", {})
|
|
706
|
+
if active.get("incomplete"):
|
|
707
|
+
return active
|
|
700
708
|
if phase in ("plan", "wait_confirm_plan"):
|
|
701
709
|
return active
|
|
702
710
|
raise RuntimeError(
|
|
@@ -294,16 +294,24 @@ def command_plan(workspace: Path | None) -> None:
|
|
|
294
294
|
raise SystemExit(str(error))
|
|
295
295
|
if active:
|
|
296
296
|
session = active.get("session", {})
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
297
|
+
session_meta = session
|
|
298
|
+
if active.get("incomplete"):
|
|
299
|
+
print("session_action=reused_incomplete")
|
|
300
|
+
print(f"session_id={session_meta.get('session_id', '')}")
|
|
301
|
+
print(f"phase={active.get('phase', '')}")
|
|
302
|
+
print("next_action=复用未完成计划会话,继续生成 discovery/plan。")
|
|
303
|
+
else:
|
|
304
|
+
print("session_action=reused")
|
|
305
|
+
print(f"session_id={session_meta.get('session_id', '')}")
|
|
306
|
+
print(f"phase={active.get('phase', '')}")
|
|
307
|
+
print("next_action=当前计划已存在,继续执行 /se:apply。")
|
|
308
|
+
return
|
|
309
|
+
else:
|
|
310
|
+
command_init(workspace)
|
|
311
|
+
config = load_workspace_config(workspace)
|
|
312
|
+
session_meta = create_session(config)
|
|
313
|
+
print("session_action=created")
|
|
314
|
+
print(f"session_id={session_meta.get('session_id', '')}")
|
|
307
315
|
command_discover(workspace)
|
|
308
316
|
args = ["--workspace", str(workspace)] if workspace else []
|
|
309
317
|
run_python("generate-smart-plan.py", args)
|