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.
- package/README.md +11 -9
- package/assets/agents/AGENTS_CORE.md +2 -2
- package/assets/docs/README.md +12 -10
- package/assets/skills/pjsdlc_architect_design/SKILL.md +4 -2
- package/assets/skills/pjsdlc_dev_sprint/SKILL.md +11 -8
- package/assets/skills/pjsdlc_implementation_doc/SKILL.md +7 -3
- package/assets/skills/pjsdlc_manager/SKILL.md +4 -4
- package/assets/skills/pjsdlc_pm_prd/SKILL.md +3 -3
- package/assets/skills/pjsdlc_release_manager/SKILL.md +2 -0
- package/assets/skills/pjsdlc_reviewer/SKILL.md +5 -2
- package/assets/skills/pjsdlc_rfc_recalibrate/SKILL.md +5 -2
- package/assets/skills/pjsdlc_tester/SKILL.md +5 -4
- package/assets/templates/IMPLEMENTATION_DOC_TEMPLATE.md +21 -7
- package/assets/templates/PLAN_TEMPLATE.yaml +27 -6
- package/assets/templates/RFC_TEMPLATE.md +12 -1
- package/assets/templates/TECH_DESIGN_TEMPLATE.md +19 -2
- package/assets/tools/build_doc_overviews.py +152 -0
- package/assets/tools/harness_utils.py +858 -0
- package/assets/tools/impact_analyzer.py +51 -0
- package/assets/tools/run_current_gate.py +29 -0
- package/assets/tools/status.py +29 -0
- package/assets/tools/transition.py +68 -0
- package/assets/tools/validate_allowed_paths.py +44 -0
- package/assets/tools/validate_design.py +199 -0
- package/assets/tools/validate_dev_state.py +20 -0
- package/assets/tools/validate_harness.py +60 -0
- package/assets/tools/validate_plan.py +24 -0
- package/assets/tools/validate_plan_draft.py +19 -0
- package/assets/tools/validate_prd.py +27 -0
- package/assets/tools/validate_prompt_language.py +138 -0
- package/assets/tools/validate_release_plan.py +37 -0
- package/assets/tools/validate_review.py +59 -0
- package/assets/tools/validate_rfc.py +105 -0
- package/assets/tools/validate_task_docs.py +40 -0
- package/assets/tools/validate_test_plan.py +82 -0
- package/dist/lib/config.js +1 -0
- package/dist/lib/migrations.js +3 -0
- package/dist/lib/sync-engine.js +4 -0
- package/dist/lib/validators.js +351 -17
- package/package.json +1 -1
- 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.
|
|
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
|
-
##
|
|
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
|
-
##
|
|
88
|
+
## 10. 与技术方案的偏移
|
|
75
89
|
|
|
76
90
|
-
|
|
77
91
|
|
|
78
|
-
##
|
|
92
|
+
## 11. 测试覆盖(Test Coverage)
|
|
79
93
|
|
|
80
94
|
| 测试(Test) | 覆盖范围(Coverage) | 结果(Result) |
|
|
81
95
|
|---|---|---|
|
|
82
96
|
| | | |
|
|
83
97
|
|
|
84
|
-
##
|
|
98
|
+
## 12. 变更记录(Change Log)
|
|
85
99
|
|
|
86
100
|
| 日期(Date) | Task ID | Commit | 摘要(Summary) |
|
|
87
101
|
|---|---|---|---|
|
|
88
102
|
| | | | |
|
|
89
103
|
|
|
90
|
-
##
|
|
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
|
|
4
|
-
#
|
|
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: "
|
|
8
|
-
# mode: "
|
|
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.
|
|
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
|
-
##
|
|
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)
|