@harmonyos-arkts/d2h 0.1.26 → 0.1.28
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/.claude-plugin/plugin.json +1 -1
- package/AGENTS.md +2 -0
- package/README.md +34 -2
- package/agents/d2h.md +24 -23
- package/dist/bin/install-opencode.js +36 -24
- package/dist/src/install-codex.js +5 -126
- package/dist/src/install-skills.js +147 -0
- package/dist/src/install-tui-extension.js +133 -0
- package/dist/src/install-vscode.js +2 -2
- package/dist/src/toolchain.js +2 -2
- package/docs/environment-check.md +3 -3
- package/package.json +6 -6
- package/skills/arkts-mvvm-architecture/SKILL.md +2 -0
- package/skills/d2h-architecture-plan/SKILL.md +21 -19
- package/skills/d2h-architecture-plan/references/architecture-plan.example.json +122 -0
- package/skills/d2h-architecture-plan/references/directory-creation.md +27 -29
- package/skills/d2h-architecture-plan/references/hybrid-design.md +13 -15
- package/skills/d2h-architecture-plan/references/json-contract.md +49 -0
- package/skills/d2h-architecture-plan/references/layout-reference.md +30 -27
- package/skills/d2h-architecture-plan/references/plan-contract.md +27 -32
- package/skills/d2h-architecture-plan/references/source-review.md +1 -1
- package/skills/d2h-architecture-plan/scripts/architecture_plan.py +147 -0
- package/skills/d2h-architecture-plan/scripts/test_architecture_plan.py +241 -0
- package/skills/d2h-dashboard/SKILL.md +12 -1
- package/skills/d2h-spec-develop/SKILL.md +52 -42
- package/skills/d2h-spec-generate/SKILL.md +30 -29
- package/skills/d2h-spec-plan/SKILL.md +20 -16
- package/skills/d2h-spec-plan/agents/openai.yaml +3 -3
- package/skills/d2h-spec-plan/references/plan-contract.md +32 -7
- package/skills/d2h-spec-plan/references/planning-method.md +12 -62
- package/skills/d2h-spec-plan/references/planning-review.md +9 -21
- package/skills/d2h-spec-plan/references/round-scope-assessment.md +12 -39
- package/skills/d2h-spec-plan/references/work-packets.md +6 -28
- package/{install-opencode.sh → tooling/install-opencode.sh} +3 -3
- package/{opencode → tooling/opencode}/interactive-tools/advance-progress-demo.mjs +1 -1
- package/{opencode → tooling/opencode}/interactive-tools/demo.mjs +3 -3
- package/{opencode → tooling/opencode}/interactive-tools/prepare-demo.mjs +1 -1
- package/{scripts → tooling/scripts}/check-environment.mjs +3 -3
- /package/{opencode → tooling/opencode}/agents.json +0 -0
- /package/{opencode → tooling/opencode}/interactive-tools/browser-host.ts +0 -0
- /package/{opencode → tooling/opencode}/interactive-tools/core.ts +0 -0
- /package/{opencode → tooling/opencode}/interactive-tools/demo-workflow.ts +0 -0
- /package/{opencode → tooling/opencode}/interactive-tools/package-lock.json +0 -0
- /package/{opencode → tooling/opencode}/interactive-tools/package.json +0 -0
- /package/{opencode → tooling/opencode}/interactive-tools/panel.ts +0 -0
- /package/{opencode → tooling/opencode}/interactive-tools/prepare-progress-demo.mjs +0 -0
- /package/{opencode → tooling/opencode}/interactive-tools/progress.ts +0 -0
- /package/{opencode → tooling/opencode}/interactive-tools/server-legacy.mjs +0 -0
- /package/{opencode → tooling/opencode}/interactive-tools/server.mjs +0 -0
- /package/{opencode → tooling/opencode}/interactive-tools/tui.tsx +0 -0
- /package/{opencode → tooling/opencode}/vscode-d2h-package.json +0 -0
- /package/{opencode → tooling/opencode}/vscode-d2h-plugin.mjs +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""Final-display contract checks. No source or HarmonyOS project is touched."""
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
import tempfile
|
|
10
|
+
import unittest
|
|
11
|
+
|
|
12
|
+
from architecture_plan import ContractError, directory_tree, load_json, validate_plan
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
SKILL_ROOT = Path(__file__).resolve().parents[1]
|
|
16
|
+
HELPER = SKILL_ROOT / "scripts" / "architecture_plan.py"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ArchitecturePlanTests(unittest.TestCase):
|
|
20
|
+
def setUp(self):
|
|
21
|
+
self.plan = load_json(SKILL_ROOT / "references" / "architecture-plan.example.json")
|
|
22
|
+
|
|
23
|
+
def nodes(self):
|
|
24
|
+
def visit(node):
|
|
25
|
+
yield node
|
|
26
|
+
for child in node["children"]:
|
|
27
|
+
yield from visit(child)
|
|
28
|
+
return list(visit(self.plan["directoryTree"]))
|
|
29
|
+
|
|
30
|
+
def node(self, path):
|
|
31
|
+
return next(node for node in self.nodes() if node["path"] == path)
|
|
32
|
+
|
|
33
|
+
def cli(self, *args, env=None):
|
|
34
|
+
return subprocess.run([sys.executable, "-X", "utf8", "-B", str(HELPER), *map(str, args)],
|
|
35
|
+
capture_output=True, encoding="utf-8", env=env, check=False)
|
|
36
|
+
|
|
37
|
+
def test_example_is_one_final_tree_with_decision_notes(self):
|
|
38
|
+
before = copy.deepcopy(self.plan)
|
|
39
|
+
validate_plan(self.plan)
|
|
40
|
+
self.assertEqual(self.plan, before)
|
|
41
|
+
self.assertEqual(set(self.plan), {"projectName", "directoryTree", "decisions"})
|
|
42
|
+
paths = directory_tree(self.plan["directoryTree"])
|
|
43
|
+
self.assertIn("entry/src/main/ets/features/mail/messageview", paths)
|
|
44
|
+
self.assertNotIn("products/thunderbird", paths)
|
|
45
|
+
self.assertTrue(self.plan["decisions"])
|
|
46
|
+
|
|
47
|
+
def test_missing_extra_and_legacy_top_fields_are_rejected(self):
|
|
48
|
+
for key in self.plan:
|
|
49
|
+
changed = copy.deepcopy(self.plan)
|
|
50
|
+
del changed[key]
|
|
51
|
+
with self.subTest(missing=key), self.assertRaises(ContractError):
|
|
52
|
+
validate_plan(changed)
|
|
53
|
+
for key in ("project", "androidAnalysis", "proposals", "recommendedProposalId", "recommendationReason",
|
|
54
|
+
"selectedProposalId", "selection", "planId", "revision", "schemaVersion", "targetRoot", "dependencies"):
|
|
55
|
+
with self.subTest(extra=key), self.assertRaises(ContractError):
|
|
56
|
+
validate_plan({**self.plan, key: None})
|
|
57
|
+
|
|
58
|
+
def test_project_name_and_decision_fields_must_be_nonempty_text(self):
|
|
59
|
+
for value in (None, True, 0, [], {}, "", " \n"):
|
|
60
|
+
with self.subTest(projectName=value), self.assertRaises(ContractError):
|
|
61
|
+
validate_plan({**self.plan, "projectName": value})
|
|
62
|
+
for key in ("title", "description"):
|
|
63
|
+
changed = copy.deepcopy(self.plan)
|
|
64
|
+
changed["decisions"][0][key] = value
|
|
65
|
+
with self.subTest(decision=key, value=value), self.assertRaises(ContractError):
|
|
66
|
+
validate_plan(changed)
|
|
67
|
+
|
|
68
|
+
def test_decisions_require_a_nonempty_exactly_shaped_array(self):
|
|
69
|
+
for value in (None, {}, "decision", [], [{}], [{"title": "保留"}],
|
|
70
|
+
[{"title": "保留", "description": "原包族", "confirmed": True}]):
|
|
71
|
+
with self.subTest(value=value), self.assertRaises(ContractError):
|
|
72
|
+
validate_plan({**self.plan, "decisions": value})
|
|
73
|
+
|
|
74
|
+
def test_old_dependency_array_and_embedded_relationship_fields_are_rejected(self):
|
|
75
|
+
for value in ([], [{"fromPath": "entry", "toPath": "AppScope", "description": "旧关系模型"}]):
|
|
76
|
+
with self.subTest(value=value), self.assertRaises(ContractError):
|
|
77
|
+
validate_plan({**self.plan, "dependencies": value})
|
|
78
|
+
for key, value in (("fromPath", "entry"), ("toPath", "AppScope"), ("dependencies", [])):
|
|
79
|
+
changed = copy.deepcopy(self.plan)
|
|
80
|
+
changed["decisions"][0][key] = value
|
|
81
|
+
with self.subTest(key=key), self.assertRaises(ContractError):
|
|
82
|
+
validate_plan(changed)
|
|
83
|
+
|
|
84
|
+
def test_nodes_require_description_children_and_known_fields(self):
|
|
85
|
+
for key in ("name", "path", "description", "children"):
|
|
86
|
+
changed = copy.deepcopy(self.plan)
|
|
87
|
+
del changed["directoryTree"][key]
|
|
88
|
+
with self.subTest(missing=key), self.assertRaises(ContractError):
|
|
89
|
+
validate_plan(changed)
|
|
90
|
+
for key, value in (("description", ""), ("children", {}), ("kind", "module"),
|
|
91
|
+
("kind", None), ("selected", True)):
|
|
92
|
+
changed = copy.deepcopy(self.plan)
|
|
93
|
+
changed["directoryTree"][key] = value
|
|
94
|
+
with self.subTest(key=key), self.assertRaises(ContractError):
|
|
95
|
+
validate_plan(changed)
|
|
96
|
+
|
|
97
|
+
def test_root_is_a_normal_directory_at_dot(self):
|
|
98
|
+
for key, value in (("path", "entry"), ("kind", "HAP")):
|
|
99
|
+
changed = copy.deepcopy(self.plan)
|
|
100
|
+
changed["directoryTree"][key] = value
|
|
101
|
+
with self.subTest(key=key), self.assertRaises(ContractError):
|
|
102
|
+
validate_plan(changed)
|
|
103
|
+
|
|
104
|
+
def test_names_and_direct_nesting_must_match_paths(self):
|
|
105
|
+
for path, name in (("entry/src/main", "main"), ("entry/src", "sources")):
|
|
106
|
+
changed = copy.deepcopy(self.plan)
|
|
107
|
+
node = changed["directoryTree"]["children"][1]["children"][0]
|
|
108
|
+
node.update(path=path, name=name)
|
|
109
|
+
with self.subTest(path=path, name=name), self.assertRaises(ContractError):
|
|
110
|
+
validate_plan(changed)
|
|
111
|
+
|
|
112
|
+
def test_unsafe_and_placeholder_paths_are_rejected(self):
|
|
113
|
+
for path in ("/absolute", "E:/target", "../source", "a/../b", "a//b", "./entry", "a\\b",
|
|
114
|
+
"pages/*", "{pages,state}", "<产品>", "…", "CON", "NUL.txt", "aux", "COM¹", "lpt².log", "a.", "a ",
|
|
115
|
+
"bad\x00path", "bad?path"):
|
|
116
|
+
changed = copy.deepcopy(self.plan)
|
|
117
|
+
changed["directoryTree"]["children"][0].update(path=path, name=path.split("/")[-1])
|
|
118
|
+
with self.subTest(path=path), self.assertRaises(ContractError):
|
|
119
|
+
validate_plan(changed)
|
|
120
|
+
|
|
121
|
+
def test_duplicate_and_case_conflicting_paths_are_rejected(self):
|
|
122
|
+
for name in ("AppScope", "appscope"):
|
|
123
|
+
changed = copy.deepcopy(self.plan)
|
|
124
|
+
duplicate = copy.deepcopy(changed["directoryTree"]["children"][0])
|
|
125
|
+
duplicate.update(name=name, path=name)
|
|
126
|
+
changed["directoryTree"]["children"].append(duplicate)
|
|
127
|
+
with self.subTest(name=name), self.assertRaisesRegex(ContractError, "duplicate/case-conflicting"):
|
|
128
|
+
validate_plan(changed)
|
|
129
|
+
|
|
130
|
+
def test_application_hap_and_expanded_production_root_are_required(self):
|
|
131
|
+
changed = copy.deepcopy(self.plan)
|
|
132
|
+
changed["directoryTree"]["children"] = []
|
|
133
|
+
with self.assertRaisesRegex(ContractError, "application HAP"):
|
|
134
|
+
validate_plan(changed)
|
|
135
|
+
self.node("entry")["kind"] = "HAR"
|
|
136
|
+
with self.assertRaisesRegex(ContractError, "application HAP"):
|
|
137
|
+
validate_plan(self.plan)
|
|
138
|
+
self.node("entry")["kind"] = "HAP"
|
|
139
|
+
self.node("entry")["children"] = []
|
|
140
|
+
with self.assertRaisesRegex(ContractError, "src/main"):
|
|
141
|
+
validate_plan(self.plan)
|
|
142
|
+
|
|
143
|
+
def test_empty_hap_production_root_is_rejected(self):
|
|
144
|
+
self.node("entry/src/main")["children"] = []
|
|
145
|
+
with self.assertRaisesRegex(ContractError, "src/main"):
|
|
146
|
+
validate_plan(self.plan)
|
|
147
|
+
|
|
148
|
+
def test_hap_tree_validation_is_not_tied_to_one_source_language(self):
|
|
149
|
+
for name in ("js", "cpp"):
|
|
150
|
+
changed = copy.deepcopy(self.plan)
|
|
151
|
+
source = next(node for node in self.nodes() if node["path"] == "entry/src/main/ets")
|
|
152
|
+
source = copy.deepcopy(source)
|
|
153
|
+
def rebase(node):
|
|
154
|
+
node["path"] = node["path"].replace("entry/src/main/ets", f"entry/src/main/{name}", 1)
|
|
155
|
+
for child in node["children"]:
|
|
156
|
+
rebase(child)
|
|
157
|
+
rebase(source)
|
|
158
|
+
source["name"] = name
|
|
159
|
+
changed["directoryTree"]["children"][1]["children"][0]["children"][0]["children"][0] = source
|
|
160
|
+
with self.subTest(source=name):
|
|
161
|
+
validate_plan(changed)
|
|
162
|
+
|
|
163
|
+
def test_module_cannot_be_inside_another_modules_source_tree(self):
|
|
164
|
+
for kind in ("HAP", "HAR", "HSP"):
|
|
165
|
+
self.node("entry/src/main/ets/features")["kind"] = kind
|
|
166
|
+
with self.subTest(kind=kind), self.assertRaisesRegex(ContractError, "source tree"):
|
|
167
|
+
validate_plan(self.plan)
|
|
168
|
+
|
|
169
|
+
def test_module_grouping_outside_a_source_tree_is_not_rejected_as_nesting(self):
|
|
170
|
+
self.node("entry")["children"].append({
|
|
171
|
+
"name": "libraries", "path": "entry/libraries", "description": "库目录分组", "children": [{
|
|
172
|
+
"name": "mail", "path": "entry/libraries/mail", "kind": "HAR",
|
|
173
|
+
"description": "由独立构建配置注册的库模块", "children": [{
|
|
174
|
+
"name": "src", "path": "entry/libraries/mail/src", "description": "库源码", "children": [{
|
|
175
|
+
"name": "main", "path": "entry/libraries/mail/src/main", "description": "生产根", "children": [{
|
|
176
|
+
"name": "ets", "path": "entry/libraries/mail/src/main/ets",
|
|
177
|
+
"description": "库的 ArkTS 源码", "children": [],
|
|
178
|
+
}],
|
|
179
|
+
}],
|
|
180
|
+
}],
|
|
181
|
+
}],
|
|
182
|
+
})
|
|
183
|
+
validate_plan(self.plan)
|
|
184
|
+
|
|
185
|
+
def test_multiple_hap_modules_and_sibling_libraries_are_supported(self):
|
|
186
|
+
for kind in ("HAP", "HAR", "HSP"):
|
|
187
|
+
with self.subTest(kind=kind):
|
|
188
|
+
changed = copy.deepcopy(self.plan)
|
|
189
|
+
module = copy.deepcopy(changed["directoryTree"]["children"][1])
|
|
190
|
+
def rebase(node):
|
|
191
|
+
node["path"] = "second" + node["path"][len("entry"):]
|
|
192
|
+
for child in node["children"]:
|
|
193
|
+
rebase(child)
|
|
194
|
+
rebase(module)
|
|
195
|
+
module.update(name="second", kind=kind)
|
|
196
|
+
changed["directoryTree"]["children"].append(module)
|
|
197
|
+
validate_plan(changed)
|
|
198
|
+
|
|
199
|
+
def test_invalid_json_duplicate_keys_and_nonstandard_numbers_are_rejected(self):
|
|
200
|
+
with tempfile.TemporaryDirectory() as temporary:
|
|
201
|
+
path = Path(temporary) / "plan.json"
|
|
202
|
+
for content in ('{"projectName": "a", "projectName": "b"}', '{"bad": NaN}',
|
|
203
|
+
'{"bad": Infinity}', '{"bad": -Infinity}', '{broken', 'null'):
|
|
204
|
+
path.write_text(content, encoding="utf-8")
|
|
205
|
+
with self.subTest(content=content), self.assertRaises(ValueError):
|
|
206
|
+
validate_plan(load_json(path))
|
|
207
|
+
|
|
208
|
+
def test_cli_validates_utf8_bom_and_makes_no_writes_or_confirmation_files(self):
|
|
209
|
+
with tempfile.TemporaryDirectory() as temporary:
|
|
210
|
+
path = Path(temporary) / "architecture-plan.json"
|
|
211
|
+
path.write_text(json.dumps(self.plan, ensure_ascii=False), encoding="utf-8-sig")
|
|
212
|
+
before = path.read_bytes()
|
|
213
|
+
result = self.cli("validate", path, env={**os.environ, "PYTHONIOENCODING": "ascii"})
|
|
214
|
+
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
|
215
|
+
self.assertEqual(json.loads(result.stdout), {"status": "valid", "projectName": "示例邮件客户端"})
|
|
216
|
+
self.assertEqual(path.read_bytes(), before)
|
|
217
|
+
self.assertEqual([p.name for p in path.parent.iterdir()], [path.name])
|
|
218
|
+
|
|
219
|
+
def test_cli_errors_do_not_rewrite_input_and_missing_file_is_reported(self):
|
|
220
|
+
with tempfile.TemporaryDirectory() as temporary:
|
|
221
|
+
path = Path(temporary) / "architecture-plan.json"
|
|
222
|
+
for content in ('{"proposals": []}', '{broken', '[]'):
|
|
223
|
+
path.write_text(content, encoding="utf-8")
|
|
224
|
+
result = self.cli("validate", path)
|
|
225
|
+
self.assertEqual(result.returncode, 1)
|
|
226
|
+
self.assertEqual(json.loads(result.stdout)["status"], "error")
|
|
227
|
+
self.assertEqual(path.read_text(encoding="utf-8"), content)
|
|
228
|
+
result = self.cli("validate", path.parent / "missing.json")
|
|
229
|
+
self.assertEqual(result.returncode, 1)
|
|
230
|
+
self.assertEqual(json.loads(result.stdout)["status"], "error")
|
|
231
|
+
|
|
232
|
+
def test_legacy_selection_commands_are_unavailable(self):
|
|
233
|
+
for command in ("apply-selection", "read-selection"):
|
|
234
|
+
result = self.cli(command, "architecture-plan.json")
|
|
235
|
+
with self.subTest(command=command):
|
|
236
|
+
self.assertNotEqual(result.returncode, 0)
|
|
237
|
+
self.assertIn("invalid choice", result.stderr)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
if __name__ == "__main__":
|
|
241
|
+
unittest.main()
|
|
@@ -19,6 +19,7 @@ npx --yes d2h-dashboard@latest --dataDir <项目>/.migration [--port 8080] [--ho
|
|
|
19
19
|
|
|
20
20
|
- `.migration/progress.json` 出现(`progress init` 后)或恢复会话已存在 → 自动启动看板并用默认浏览器打开 URL,不等用户请求;每轮会话执行一次,复用实例不重复启动、不重复打开。
|
|
21
21
|
- 启动或打开失败只记录不阻塞迁移,下次阶段更新重试一次。
|
|
22
|
+
- 确认 `$url` 后按「TUI 常驻横幅」一节更新横幅;端口漂移(重启落到新端口)同样触发更新。
|
|
22
23
|
|
|
23
24
|
## 后台启动与验证(必做)
|
|
24
25
|
|
|
@@ -53,7 +54,17 @@ do {
|
|
|
53
54
|
|
|
54
55
|
**URL 告知规范**:URL 单独成行或用反引号包裹,其后不得紧跟任何文字(含中文括号说明)——否则 markdown 会把 URL+后缀渲染成单个失效链接,说明放下一行。
|
|
55
56
|
|
|
57
|
+
## TUI 常驻横幅
|
|
58
|
+
|
|
59
|
+
- 确认 `$url` 后(启动/复用/端口漂移)执行,10s 超时防卡死:
|
|
60
|
+
```powershell
|
|
61
|
+
Start-Process cmd.exe -ArgumentList '/c opencode-tui-extension.cmd set "<url>"' -NoNewWindow -PassThru | Wait-Process -Timeout 10 -ErrorAction SilentlyContinue
|
|
62
|
+
```
|
|
63
|
+
bash:`timeout 10 opencode-tui-extension set "<url>"`
|
|
64
|
+
- 停止看板后同法执行 `clear`
|
|
65
|
+
- 失败/超时/命令不存在:只记录不阻塞,降级为 URL 单独成行输出;安装注册由 `d2h install` 负责,本 skill 不做初始化
|
|
66
|
+
|
|
56
67
|
## 复用与停止
|
|
57
68
|
|
|
58
69
|
- **复用**:启动前探测 8080–8090 `GET /api/data/manifest`(1s 超时),`root` == 目标 dataDir 且 `GET /` 返回 200 才复用该 URL;仅 root 匹配但 `GET /` 非 200 属损坏实例(资产被删/版本切换残留),按停止规则杀掉后重新启动。root 不匹配跳过(其他流程的服务器)。服务器常驻,会话结束不回收,残留进程属预期。
|
|
59
|
-
- **停止**:仅用户显式要求(「关闭看板」「关闭 XX 项目看板」)才执行,且必须 root 归属校验通过:Windows `Get-NetTCPConnection -LocalPort <port> -State Listen | ForEach-Object { Stop-Process -Id $_.OwningProcess }`(杀 node 真实 PID,cmd 包装 PID 无效);bash `kill <pid>`。未找到 → 报告未在运行;root 不匹配 →
|
|
70
|
+
- **停止**:仅用户显式要求(「关闭看板」「关闭 XX 项目看板」)才执行,且必须 root 归属校验通过:Windows `Get-NetTCPConnection -LocalPort <port> -State Listen | ForEach-Object { Stop-Process -Id $_.OwningProcess }`(杀 node 真实 PID,cmd 包装 PID 无效);bash `kill <pid>`。未找到 → 报告未在运行;root 不匹配 → 跳过并说明属于其他流程。停止成功后按「TUI 常驻横幅」执行 `clear`。
|
|
@@ -1,50 +1,53 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: d2h-
|
|
3
|
-
description: Implement ordered Android-to-HarmonyOS page tasks from pages.json, batch_report.
|
|
2
|
+
name: d2h-spec-develop
|
|
3
|
+
description: Implement ordered Android-to-HarmonyOS page tasks from pages.json, batch_report.json, and page-scoped spec.md files. Use during page development when a dedicated page-worker must complete one page's ArkTS implementation and managed build; the parent agent owns testing and fixes, rebuilding, evidence, commits, and final acceptance before the next page starts.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Page development loop
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Each page-worker implements only the selected page and brings its build to a pass. After development is complete, the parent agent takes over testing, defect fixes, and acceptance for the current task. A worker may be reused for a fixed ordered group of up to three related Specs. Implement one assigned task per handoff; the parent accepts it before authorizing the same worker to continue with the next member. Task identities, states, evidence and acceptance remain separate. Reuse is conditional on the host supporting child-session continuation and the parent confirming that the next task shares relevant context, the worker can use the refreshed baseline and interfaces, and no unresolved task or ownership conflict remains. Otherwise, explicitly revise the group and dispatch a fresh worker for the remaining scope.
|
|
9
9
|
|
|
10
10
|
## Inputs and boundaries
|
|
11
11
|
|
|
12
|
-
- Use
|
|
12
|
+
- Use `batch_report.json` for task identities, dependencies, planning and execution state. Read included tasks and execution order from its bound planning field; legacy ledgers without planning use tasks order only when no selected scope is supplied. Do not read plan.json for execution or recovery. Keep task IDs and page packages unchanged.
|
|
13
13
|
- Use the selected page package's `spec.md` as the authority for page scope, behavior, acceptance sequences, Android evidence, and expected HarmonyOS files.
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
14
|
+
- When a development scope is supplied, require bound ledger planning and execute only its included tasks. Never select an excluded task because its Spec exists or its ledger state is pending. Return mixed selected/unselected behavior in one Spec as a scope conflict before implementation.
|
|
15
|
+
- Read `.migration/utg/page.json` when the Spec references a UTG node or the arrival path needs clarification. Read cited Android source before implementing or fixing behavior.
|
|
16
|
+
- Each `page_NNNN_<name>-<id>/` directory contains only `spec.md`. Do not add `ui-design.md`, copied screenshots, copied `view.xml`, or test/build evidence to page packages. Keep evidence in the resolved task evidence directory.
|
|
17
|
+
- Do not regenerate `pages.json`, page Specs, or `batch_report.json` during ordinary development. Correct an owning artifact only when current source proves it wrong, record the reason, and preserve execution history.
|
|
17
18
|
- Use `droid2hmos` managed build and test entry points. Do not use task-planning commands or static scripts to choose or execute page work.
|
|
18
|
-
- Preserve unrelated user changes. Never reset, discard, or include them in a page commit.
|
|
19
|
+
- Preserve unrelated user changes throughout implementation, parent fixes, and commits. Never reset, discard, or include them in a page commit.
|
|
19
20
|
|
|
20
21
|
## Parent-agent mode
|
|
21
22
|
|
|
22
|
-
1. Read `pages.json` and `batch_report.
|
|
23
|
-
2. Select the earliest
|
|
23
|
+
1. Read `pages.json` and `batch_report.json`; verify task identities, Spec paths and dependencies against pages.json, and use ledger planning for development scope and execution order. Verify planning references, counts and states. Reject missing, stale or invalid planning instead of falling back to all tasks when a selected scope or planning is present.
|
|
24
|
+
2. Select the earliest eligible task in planning milestone/wave order (or legacy ledger tasks order when allowed) whose development/build/validation states are not `implemented/passed/passed` and whose declared dependencies are satisfied. When bound planning is present, the parent may group it with adjacent unfinished tasks in the same wave that share page, module or component context, normally two or three tasks and never more than three. Fix the ordered task IDs before dispatch; keep complex or unclear work as a single task and verify dependencies again before each member starts.
|
|
24
25
|
3. Resolve the page package, Android root, HarmonyOS root, repository root, current `HEAD`, Git status, device target, and task evidence directory.
|
|
25
|
-
4. Dispatch exactly one page-worker with absolute paths,
|
|
26
|
-
5.
|
|
27
|
-
6.
|
|
28
|
-
7. Only after the
|
|
29
|
-
8. Only when the user explicitly requests it, enumerate every Python Hypium test list and run one managed batch regression. Append the batch evidence to `batch_report.md` and the migration report.
|
|
26
|
+
4. Dispatch exactly one page-worker with absolute paths, the current task ID, ordered group task IDs when present, dispatch baseline, device target, and task evidence directory. Do not run page-workers in parallel. Wait for its result before the parent begins testing and fixes. Preserve the returned child-session identifier for possible continuation; use the host's actual resume parameter and confirm that the continuation returns the same session ID. A new child session is a fresh dispatch, not context reuse.
|
|
27
|
+
5. After the worker returns, the parent takes ownership of the current page and executes testing and all fixes according to **Managed page tests and fixes** below until current build and required tests pass.
|
|
28
|
+
6. Execute **Parent report and commits** below.
|
|
29
|
+
7. Apply **Parent verification** below. Only after the current page reaches `implemented/passed/passed`, evidence is current, and all page-owned changes are committed may the parent authorize the same worker to implement the next group member. Refresh its task package, baseline, permitted paths, dependency evidence and evidence directory. If continuation is unsupported or reuse review fails, explicitly revise the group and dispatch a fresh worker for the next eligible task with these refreshed inputs; lack of reuse support must not block normal serial development. If a member is blocked or the group becomes invalid, return to the parent with remaining tasks unchanged; do not skip members or expand the group. Dispatch a worker for a new group only after the current group closes or is explicitly revised.
|
|
30
30
|
|
|
31
31
|
Use this worker prompt contract:
|
|
32
32
|
|
|
33
33
|
```text
|
|
34
|
-
Use $d2h-
|
|
34
|
+
Use $d2h-spec-develop in page-worker mode. Complete the initial implementation and managed build for exactly <TASK_ID>.
|
|
35
|
+
Assigned task IDs: <fixed ordered group of 1–3 task IDs; implement only TASK_ID until parent acceptance and explicit continuation>
|
|
36
|
+
Skill path: <absolute path to this SKILL.md>
|
|
35
37
|
Migration root: <absolute path>
|
|
36
38
|
Page package: <absolute path>
|
|
37
39
|
Android root: <absolute path>
|
|
38
40
|
HarmonyOS root: <absolute path>
|
|
39
41
|
Repository root: <absolute path>
|
|
40
42
|
Dispatch baseline: <HEAD and recorded Git status>
|
|
43
|
+
Execution ledger: <absolute batch_report.json path; planning revision/hash, milestone ID, wave ID when present>
|
|
41
44
|
Device target: <target>
|
|
42
45
|
Evidence directory: <absolute task/run directory>
|
|
43
46
|
|
|
44
47
|
Own the complete page loop: read spec.md, UTG evidence and cited Android source;
|
|
45
|
-
implement the page; run managed build;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
implement the page; run managed build; update the migration report and batch_report.json in place;
|
|
49
|
+
create scoped commits; Do not start another page.
|
|
50
|
+
After handoff, the parent owns all testing, defect fixes, rebuilding, and acceptance.
|
|
48
51
|
```
|
|
49
52
|
|
|
50
53
|
If independent page-worker execution is unavailable, return that the required mode cannot run. Do not silently implement multiple pages in the parent agent.
|
|
@@ -53,9 +56,9 @@ If independent page-worker execution is unavailable, return that the required mo
|
|
|
53
56
|
|
|
54
57
|
### 1. Claim and understand the page
|
|
55
58
|
|
|
56
|
-
- Confirm the task is the earliest incomplete eligible
|
|
59
|
+
- Confirm the task is the earliest incomplete eligible task in ledger planning (or legacy task order when allowed) and no other page is being implemented or awaiting parent acceptance.
|
|
57
60
|
- Compare the checkout with the dispatch baseline. Preserve and avoid unrelated dirty files; if ownership conflicts with a build-affecting file, return the exact conflict instead of overwriting it.
|
|
58
|
-
- Mark only the selected task `in_progress` in
|
|
61
|
+
- Mark only the selected task `in_progress` in `batch_report.json`.
|
|
59
62
|
- Read the complete `spec.md`, the referenced `.migration/utg/page.json` node, every cited Fact, and every cited Android source file. Follow handlers, state transitions, repositories, navigation, persistence, error paths, and platform side effects far enough to establish the observable result.
|
|
60
63
|
- Inspect the HarmonyOS architecture, routes, resources, shared components, tests, and current implementation. Load `arkts-mvvm-architecture` before planning or writing ArkTS.
|
|
61
64
|
- Build a private checklist mapping every `F-xxx` to reachable code, a stable semantic locator, an executed test assertion, and an observable result. Do not create another planning file.
|
|
@@ -63,10 +66,11 @@ If independent page-worker execution is unavailable, return that the required mo
|
|
|
63
66
|
|
|
64
67
|
### 2. Implement
|
|
65
68
|
|
|
66
|
-
- Implement all page-scoped UI, navigation, overlays, state transitions, persistence/data effects, error paths,
|
|
69
|
+
- Implement all page-scoped UI and UI resources, navigation, overlays, state transitions, persistence/data effects, error paths, and required shared integration.
|
|
67
70
|
- Use the Android source and observable behavior for functional truth. Use HarmonyOS-native architecture and components for the target implementation.
|
|
68
71
|
- Give tested interactive elements stable, unique semantic locators. Do not add controls solely to make testing easier.
|
|
69
|
-
- Load the applicable domain Skill for identity, i18n, drawable conversion, icon sizing, or third-party substitution
|
|
72
|
+
- Load the applicable domain Skill for identity, i18n, drawable conversion, icon sizing, or third-party substitution.
|
|
73
|
+
- Before implementing platform capabilities or uncertain HarmonyOS APIs (`@kit.*` Kit imports or `@ohos.*` modules), and when build or test failures point to API signatures or behavior, load `d2h-cloud-knowledge`.
|
|
70
74
|
- Keep shared changes limited to capabilities this page actually needs and record downstream consumers.
|
|
71
75
|
- Do not leave stubs, fixed success values, swallowed errors, unreachable pages, placeholder controls, or TODO implementations.
|
|
72
76
|
|
|
@@ -74,12 +78,20 @@ If independent page-worker execution is unavailable, return that the required mo
|
|
|
74
78
|
|
|
75
79
|
- Run a fresh, non-interactive, bounded build through `droid2hmos build harmony <ANDROID_PROJECT>` using the resolved project configuration.
|
|
76
80
|
- Save the exact command, exit status, timestamps, relevant log, produced HAP path, and HAP SHA-256 in the task evidence directory.
|
|
77
|
-
-
|
|
81
|
+
- Fix compilation errors caused or exposed by the implementation within this worker until the build passes.
|
|
78
82
|
- An older HAP, static analysis result, direct Hvigor run, or previous log is not current build evidence.
|
|
79
83
|
|
|
80
|
-
### 4.
|
|
84
|
+
### 4. Worker report and commits
|
|
81
85
|
|
|
82
|
-
- Load `
|
|
86
|
+
- Load `maintaining-migration-report` while evidence is fresh. Record real HarmonyOS files, Android source provenance, current managed evidence, differences, and source-proven discoveries; run report validation.
|
|
87
|
+
- After a successful build, inspect Git status and diff and create a scoped initial implementation commit containing only this page's implementation, resources, required shared files, and source-proven Spec corrections. Do not commit unrelated changes.
|
|
88
|
+
- Update the existing `batch_report.json` task object, summary, evidence, files, implementation commit, timestamps, blockers, and append-only history in place. Do not rerun `build_batch_report.py`. When planning is present, record planning_ref with revision, sha256, milestone_id and wave_id in this run's task history. Preserve planning and planning_history when updating task results.
|
|
89
|
+
- Set `implemented/passed/not_run` only when all three states are supported by current evidence. Use AI inspection to confirm task/summary/history consistency and preserve all prior history.
|
|
90
|
+
- Return the handoff and stop modifying this page. Do not generate or execute tests or select the next page.
|
|
91
|
+
|
|
92
|
+
## Managed page tests and fixes
|
|
93
|
+
|
|
94
|
+
- The parent agent loads `d2h-hypium-forge` to perform testing. Add or update executable Python Hypium tests for every `F-xxx`, with paired `.py` and `.json` test-list assets and stable semantic locators.
|
|
83
95
|
- Cover navigation or overlay lifecycle, state changes, data effects, and error behavior required by the Spec. Assertions must prove the declared result, not merely that a page or button exists.
|
|
84
96
|
- Run only the current page's test lists (the `pageNNNN_*` lists owned by this task) through the managed entry point. Do not append regression lists from other pages.
|
|
85
97
|
|
|
@@ -92,35 +104,33 @@ If independent page-worker execution is unavailable, return that the required mo
|
|
|
92
104
|
|
|
93
105
|
- Save the exact command, result, native report/log, device target, timestamps, hashes, and returned `managedEvidence` in the task evidence directory.
|
|
94
106
|
- A written-but-unexecuted test, screenshot-only check, coordinate-only replay, direct local runner, old log, or zero-test result is not a pass.
|
|
95
|
-
-
|
|
107
|
+
- After application fixes, repeat managed build, installation, and managed tests until current build and required tests pass. Only persistent external environment, signing, or device failures may become `blocked` after bounded retries and recorded diagnostics.
|
|
96
108
|
|
|
97
|
-
|
|
109
|
+
## Parent report and commits
|
|
98
110
|
|
|
99
111
|
- Load `maintaining-migration-report` while evidence is fresh. Record real HarmonyOS files, Android source provenance, current managed evidence, differences, and source-proven discoveries; run report validation.
|
|
100
|
-
- Inspect
|
|
101
|
-
-
|
|
102
|
-
-
|
|
103
|
-
- Update the existing `batch_report.md` queue row, detail section, counts, evidence, files, implementation commit, timestamp, blocker, and chronological notes in place. Do not rerun `build_batch_report.py`.
|
|
104
|
-
- Set `implemented/passed/passed` only when all three states are supported by current evidence. Use AI inspection to confirm queue/detail/count consistency and preserve all prior history.
|
|
112
|
+
- Inspect the final diff. After current build and tests pass, commit the parent's page fixes, test files, and necessary shared changes.
|
|
113
|
+
- Update the existing `batch_report.json` task object, summary, evidence, files, implementation commit, timestamps, blockers, and append-only history in place. Do not rerun `build_batch_report.py`. When planning is present, record planning_ref with revision, sha256, milestone_id and wave_id in this run's task history. Preserve planning and planning_history when updating task results.
|
|
114
|
+
- Set `implemented/passed/passed` only when all three states are supported by current evidence. Use AI inspection to confirm task/summary/history consistency and preserve all prior history.
|
|
105
115
|
- Commit the ledger and immutable task evidence separately from the implementation commit. Do not create a self-referential commit field.
|
|
106
116
|
|
|
107
117
|
## Parent verification
|
|
108
118
|
|
|
109
|
-
Before
|
|
119
|
+
Before dispatching a worker for the next page, the parent confirms:
|
|
110
120
|
|
|
111
|
-
- every `F-xxx` has reachable implementation, a stable locator, and an actually executed assertion;
|
|
121
|
+
- every dispatched `F-xxx` has reachable implementation, a stable locator, and an actually executed assertion;
|
|
112
122
|
- the page is reachable from the real app entry without a test-only route or replacement control;
|
|
113
|
-
- build logs, HAP hash, test reports, managed evidence,
|
|
114
|
-
- `batch_report.
|
|
115
|
-
-
|
|
116
|
-
- user changes and other pages were not reset or included;
|
|
123
|
+
- final build logs, installed HAP hash, source snapshot, test reports, managed evidence, migration-report refs, and recorded implementation commit correspond to the final tested application;
|
|
124
|
+
- `batch_report.json` is internally consistent and the selected task row is `implemented/passed/passed`;
|
|
125
|
+
- all page-owned implementation, tests, report, ledger, and evidence changes are committed, while unrelated user changes remain untouched;
|
|
117
126
|
- no implementation defect was reclassified as a platform difference or external blocker.
|
|
118
127
|
|
|
119
|
-
|
|
128
|
+
If any check fails, the parent continues fixing the issues and repeats the necessary build/install/test and reporting steps.
|
|
120
129
|
|
|
121
130
|
## Batch regression
|
|
122
131
|
|
|
123
|
-
Run the batch regression only when the user explicitly requests it. During ordinary page development, run only the current page's test lists
|
|
132
|
+
Run the batch regression only when the user explicitly requests it. During ordinary page development, run only the current page's test lists.
|
|
124
133
|
|
|
125
134
|
## Worker result
|
|
126
|
-
|
|
135
|
+
|
|
136
|
+
Return the task ID and page, final states, implementation and test files, managed build command/HAP/evidence, implementation and ledger commit IDs, unrelated Git changes left untouched, and any genuine blocker or unresolved item.
|