agent-project-sdlc 0.1.18 → 0.1.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.
Files changed (41) hide show
  1. package/README.md +11 -9
  2. package/assets/agents/AGENTS_CORE.md +2 -2
  3. package/assets/docs/README.md +12 -10
  4. package/assets/skills/pjsdlc_architect_design/SKILL.md +4 -2
  5. package/assets/skills/pjsdlc_dev_sprint/SKILL.md +11 -8
  6. package/assets/skills/pjsdlc_implementation_doc/SKILL.md +7 -3
  7. package/assets/skills/pjsdlc_manager/SKILL.md +4 -4
  8. package/assets/skills/pjsdlc_pm_prd/SKILL.md +3 -3
  9. package/assets/skills/pjsdlc_release_manager/SKILL.md +2 -0
  10. package/assets/skills/pjsdlc_reviewer/SKILL.md +5 -2
  11. package/assets/skills/pjsdlc_rfc_recalibrate/SKILL.md +5 -2
  12. package/assets/skills/pjsdlc_tester/SKILL.md +5 -4
  13. package/assets/templates/IMPLEMENTATION_DOC_TEMPLATE.md +21 -7
  14. package/assets/templates/PLAN_TEMPLATE.yaml +27 -6
  15. package/assets/templates/RFC_TEMPLATE.md +12 -1
  16. package/assets/templates/TECH_DESIGN_TEMPLATE.md +19 -2
  17. package/assets/tools/build_doc_overviews.py +152 -0
  18. package/assets/tools/harness_utils.py +858 -0
  19. package/assets/tools/impact_analyzer.py +51 -0
  20. package/assets/tools/run_current_gate.py +29 -0
  21. package/assets/tools/status.py +29 -0
  22. package/assets/tools/transition.py +68 -0
  23. package/assets/tools/validate_allowed_paths.py +44 -0
  24. package/assets/tools/validate_design.py +199 -0
  25. package/assets/tools/validate_dev_state.py +20 -0
  26. package/assets/tools/validate_harness.py +60 -0
  27. package/assets/tools/validate_plan.py +24 -0
  28. package/assets/tools/validate_plan_draft.py +19 -0
  29. package/assets/tools/validate_prd.py +27 -0
  30. package/assets/tools/validate_prompt_language.py +138 -0
  31. package/assets/tools/validate_release_plan.py +37 -0
  32. package/assets/tools/validate_review.py +59 -0
  33. package/assets/tools/validate_rfc.py +105 -0
  34. package/assets/tools/validate_task_docs.py +40 -0
  35. package/assets/tools/validate_test_plan.py +82 -0
  36. package/dist/lib/config.js +1 -0
  37. package/dist/lib/migrations.js +3 -0
  38. package/dist/lib/sync-engine.js +4 -0
  39. package/dist/lib/validators.js +351 -17
  40. package/package.json +1 -1
  41. package/source-mappings.yaml +6 -0
@@ -50,10 +50,24 @@ Input
50
50
  - Config Contract:
51
51
  - Testing Handoff Readiness:
52
52
  - Known Missing Runtime Boundaries:
53
- - Basic Self-test Evidence:
53
+ - Basic Self-test Evidence: See `Development Self-Test Report`.
54
54
  - Not applicable:
55
55
 
56
- ## 7. Testing Handoff Contract(测试交接合同)
56
+ ## 7. Development Self-Test Report(开发自测报告,已执行)
57
+
58
+ - Contract Source:
59
+ - Scenario Results:
60
+ - Executed Gates:
61
+ - Module Key Test Path: local start / invocation -> all self-test scenarios -> all task/module promised runnable entries -> actual internal key paths / boundaries / checkpoints -> observable completion evidence
62
+ - Actual Evidence:
63
+ - Missing / Blockers:
64
+ - Testing Handoff Readiness:
65
+
66
+ | Scenario ID | Result | Executed Entry | Actual Exit | Evidence |
67
+ |---|---|---|---|---|
68
+ | ST-001 | PASS / BLOCKED | | | |
69
+
70
+ ## 8. Testing Handoff Contract(测试交接合同)
57
71
 
58
72
  - Entry:
59
73
  - Config:
@@ -63,7 +77,7 @@ Input
63
77
  - Cleanup / reset / idempotency:
64
78
  - Evidence Level:
65
79
 
66
- ## 8. 关键实现逻辑
80
+ ## 9. 关键实现逻辑
67
81
 
68
82
  - 输入校验(Input validation):
69
83
  - 核心分支(Core branches):
@@ -71,22 +85,22 @@ Input
71
85
  - 边界兜底(Boundary fallback):
72
86
  - 性能或并发注意事项(Performance or concurrency notes):
73
87
 
74
- ## 9. 与技术方案的偏移
88
+ ## 10. 与技术方案的偏移
75
89
 
76
90
  -
77
91
 
78
- ## 10. 测试覆盖(Test Coverage)
92
+ ## 11. 测试覆盖(Test Coverage)
79
93
 
80
94
  | 测试(Test) | 覆盖范围(Coverage) | 结果(Result) |
81
95
  |---|---|---|
82
96
  | | | |
83
97
 
84
- ## 11. 变更记录(Change Log)
98
+ ## 12. 变更记录(Change Log)
85
99
 
86
100
  | 日期(Date) | Task ID | Commit | 摘要(Summary) |
87
101
  |---|---|---|---|
88
102
  | | | | |
89
103
 
90
- ## 12. 后续维护注意事项
104
+ ## 13. 后续维护注意事项
91
105
 
92
106
  -
@@ -1,17 +1,20 @@
1
1
  current_task_id: "TASK-001"
2
2
  next_task_sequence: 2
3
- # Optional. Omit this block for normal serial workflow. Only add it when the
4
- # user explicitly asks for parallel / multi-agent / multi-worktree execution.
3
+ # Optional top-level execution contract. Omit this block when the current task
4
+ # stays serial after the default parallel eligibility check. Use
5
+ # trigger: "workflow_default" when the workflow safely splits the task by
6
+ # default; use trigger: "user_requested" when the user explicitly asks for
7
+ # parallel / multi-agent / multi-worktree execution.
5
8
  # parallel_execution:
6
9
  # enabled: true
7
- # trigger: "user_requested"
8
- # mode: "user_orchestrated" # or "runtime_managed" when the current agent runtime can spawn subagents
10
+ # trigger: "workflow_default"
11
+ # mode: "runtime_managed"
12
+ # runtime:
13
+ # provider: "codex_native_subagents" # fallbacks: user_orchestrated, codex_exec_worktree
9
14
  # coordinator: "main_agent"
10
15
  # workers:
11
16
  # - id: "worker-feature"
12
17
  # writes_repo: true
13
- # branch: "agent/feature"
14
- # worktree: "../project-feature"
15
18
  # owned_paths:
16
19
  # - "src/feature/**"
17
20
  # - "tests/feature/**"
@@ -63,6 +66,24 @@ tasks:
63
66
  kind: "local | ci | staging | cloud_vm | managed_service | browser | worker | not_applicable"
64
67
  required_for_done: true
65
68
  handoff_entrypoint: "URL / CLI / worker command / server action"
69
+ # Required for SPRINTING tasks with a runnable boundary. Created during
70
+ # ARCHITECTING/RFC and executed during SPRINTING.
71
+ self_test_contract:
72
+ status: "required | not_applicable"
73
+ source: ".docs/03_tech_plan/example.md"
74
+ capability_refs:
75
+ - "PRD-EXAMPLE-001"
76
+ runnable_entry: "command / URL / endpoint / worker command"
77
+ observable_exit: "response / page state / side effect / log / artifact"
78
+ module_key_test_path: "local start command / URL -> all self-test scenarios -> all task/module promised runnable entries -> internal key paths / boundaries / checkpoints -> observable completion evidence"
79
+ required_gates:
80
+ - "npm run smoke"
81
+ scenarios:
82
+ - id: "ST-001"
83
+ entry: "command / URL / endpoint / worker command"
84
+ expected_exit: "observable response / side effect / page state"
85
+ evidence: "command/browser/API/log/screenshot/etc"
86
+ not_applicable_reason: ""
66
87
  working_notes:
67
88
  - "执行现场备注只在 open task 保留。"
68
89
  result_docs:
@@ -38,6 +38,17 @@
38
38
  - Retained test docs:
39
39
  - Reason:
40
40
 
41
- ## 8. Status
41
+ ## 8. Development Self-Test Impact(开发自测影响)
42
+
43
+ - Entry/exit impact:
44
+ - Runtime / target environment impact:
45
+ - Required gates impact:
46
+ - Tech plan self-test contract impact:
47
+ - `plan.yaml` / `plan.draft.yaml` task contract impact:
48
+ - Implementation doc self-test report impact:
49
+ - Module key test path impact:
50
+ - Review / Testing handoff impact:
51
+
52
+ ## 9. Status
42
53
 
43
54
  - Status: DRAFT
@@ -45,12 +45,29 @@ Input
45
45
 
46
46
  `Implementation Doc` 应指向模块、子系统或核心数据流级文档;多个 task 可以更新同一份文档,task id 和 commit 记录在该文档的 provenance / Change Log 中。
47
47
 
48
- ## 7. 风险与缓解
48
+ ## 7. Development Self-Test Contract(开发自测合同,待执行)
49
+
50
+ > service / agent / runtime / worker / frontend app / provider-live / API / CLI 等可运行边界的开发 task 必填,并同步写入 `plan.draft.yaml.tasks[].self_test_contract`。
51
+
52
+ - Contract source:
53
+ - Capability refs:
54
+ - Runnable entry:
55
+ - Observable exit:
56
+ - Module key test path(local start -> all self-test scenarios -> all promised runnable entries -> internal key paths -> observable completion evidence):
57
+ - Required gates:
58
+
59
+ | Scenario ID | Entry | Expected Exit | Evidence |
60
+ |---|---|---|---|
61
+ | ST-001 | | | command/browser/API/log/screenshot/etc |
62
+
63
+ - Not applicable reason:
64
+
65
+ ## 8. 风险与缓解
49
66
 
50
67
  | 风险(Risk) | 等级(Level) | 缓解措施(Mitigation) |
51
68
  |---|---|---|
52
69
  | | P1 | |
53
70
 
54
- ## 8. 需要关注的方案偏移
71
+ ## 9. 需要关注的方案偏移
55
72
 
56
73
  -
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import hashlib
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ from harness_utils import ROOT, HarnessError, require, run_main
10
+
11
+
12
+ DOCS_ROOT = ROOT / ".docs"
13
+ OUTPUT_NAME = "overview.md"
14
+ LEGACY_OUTPUT_NAME = "overview.html"
15
+
16
+
17
+ def stage_dirs() -> list[Path]:
18
+ require(DOCS_ROOT.exists(), "Missing .docs directory")
19
+ return sorted(path for path in DOCS_ROOT.iterdir() if path.is_dir() and not path.name.startswith("."))
20
+
21
+
22
+ def resolve_stage(value: str) -> Path:
23
+ raw = Path(value)
24
+ path = raw if raw.is_absolute() else ROOT / raw
25
+ if not path.exists() and not value.startswith(".docs/"):
26
+ path = DOCS_ROOT / value
27
+ require(path.exists() and path.is_dir(), f"Stage directory not found: {value}")
28
+ require(DOCS_ROOT in path.parents or path == DOCS_ROOT, f"Stage must be under .docs/: {value}")
29
+ return path
30
+
31
+
32
+ def markdown_files(stage: Path) -> list[Path]:
33
+ files = []
34
+ for path in sorted(stage.rglob("*.md")):
35
+ if any(part.startswith(".") for part in path.relative_to(stage).parts):
36
+ continue
37
+ if path.name in {OUTPUT_NAME, LEGACY_OUTPUT_NAME}:
38
+ continue
39
+ files.append(path)
40
+ return files
41
+
42
+
43
+ def source_hash(files: list[Path], stage: Path) -> str:
44
+ digest = hashlib.sha256()
45
+ for path in files:
46
+ relative = path.relative_to(stage).as_posix()
47
+ digest.update(relative.encode("utf-8"))
48
+ digest.update(b"\0")
49
+ digest.update(path.read_bytes())
50
+ digest.update(b"\0")
51
+ return digest.hexdigest()[:16]
52
+
53
+
54
+ def markdown_link(label: str, target: str) -> str:
55
+ escaped_label = label.replace("[", "\\[").replace("]", "\\]")
56
+ escaped_target = target.replace(" ", "%20")
57
+ return f"[{escaped_label}]({escaped_target})"
58
+
59
+
60
+ def markdown_document(stage: Path, files: list[Path]) -> str:
61
+ stage_name = stage.relative_to(ROOT).as_posix()
62
+ digest = source_hash(files, stage)
63
+ parts = [
64
+ f"# {stage_name} overview",
65
+ "",
66
+ "<!-- generated-by: AI SDLC Harness build_doc_overviews.py -->",
67
+ f"<!-- source-hash: {digest} -->",
68
+ "",
69
+ "Generated artifact. Markdown slices remain the source of truth.",
70
+ "",
71
+ f"Source hash: `{digest}`",
72
+ "",
73
+ "## Source Slices",
74
+ "",
75
+ ]
76
+
77
+ if not files:
78
+ parts.extend(["当前阶段还没有 Markdown slice。", ""])
79
+ else:
80
+ for index, path in enumerate(files, start=1):
81
+ relative = path.relative_to(stage).as_posix()
82
+ parts.append(f"{index}. {markdown_link(relative, relative)}")
83
+ parts.append("")
84
+
85
+ for path in files:
86
+ relative = path.relative_to(stage).as_posix()
87
+ content = path.read_text(encoding="utf-8").strip()
88
+ parts.extend(
89
+ [
90
+ "---",
91
+ "",
92
+ f"## {relative}",
93
+ "",
94
+ f"Source: {markdown_link(relative, relative)}",
95
+ "",
96
+ content if content else "空文档。",
97
+ "",
98
+ ]
99
+ )
100
+
101
+ return "\n".join(parts).rstrip() + "\n"
102
+
103
+
104
+ def write_overview(stage: Path, check: bool) -> bool:
105
+ files = markdown_files(stage)
106
+ output = stage / OUTPUT_NAME
107
+ legacy_output = stage / LEGACY_OUTPUT_NAME
108
+ rendered = markdown_document(stage, files)
109
+ if check:
110
+ if legacy_output.exists():
111
+ print(f"Legacy {legacy_output.relative_to(ROOT)} remains")
112
+ return False
113
+ if not output.exists():
114
+ print(f"Missing {output.relative_to(ROOT)}")
115
+ return False
116
+ existing = output.read_text(encoding="utf-8")
117
+ if existing != rendered:
118
+ print(f"Outdated {output.relative_to(ROOT)}")
119
+ return False
120
+ print(f"OK {output.relative_to(ROOT)}")
121
+ return True
122
+ if legacy_output.exists():
123
+ legacy_output.unlink()
124
+ output.write_text(rendered, encoding="utf-8")
125
+ print(f"Wrote {output.relative_to(ROOT)}")
126
+ return True
127
+
128
+
129
+ def selected_stages(args: argparse.Namespace) -> list[Path]:
130
+ if args.stage:
131
+ return [resolve_stage(value) for value in args.stage]
132
+ return stage_dirs()
133
+
134
+
135
+ def main() -> None:
136
+ parser = argparse.ArgumentParser(description="Build deterministic Markdown overviews for .docs stage slices")
137
+ parser.add_argument("--stage", action="append", help="Stage directory, e.g. .docs/01_product or 01_product")
138
+ parser.add_argument("--all", action="store_true", help="Build all .docs stage directories")
139
+ parser.add_argument("--check", action="store_true", help="Check whether overview.md files are up to date")
140
+ args = parser.parse_args()
141
+
142
+ stages = selected_stages(args)
143
+ require(stages, "No .docs stage directories found")
144
+ ok = True
145
+ for stage in stages:
146
+ ok = write_overview(stage, args.check) and ok
147
+ if not ok:
148
+ sys.exit(1)
149
+
150
+
151
+ if __name__ == "__main__":
152
+ run_main(main)