@frankzhang2026/opencode-android-orchestrator 0.2.0 → 0.3.0

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 (50) hide show
  1. package/CHANGELOG.md +18 -1
  2. package/README.md +50 -39
  3. package/THIRD_PARTY_NOTICES.md +1 -1
  4. package/dist/cli.js +127 -10
  5. package/dist/cli.js.map +1 -1
  6. package/dist/doctor/installation.d.ts.map +1 -1
  7. package/dist/doctor/installation.js +29 -4
  8. package/dist/doctor/installation.js.map +1 -1
  9. package/dist/index.d.ts +2 -2
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +2 -2
  12. package/dist/index.js.map +1 -1
  13. package/dist/installer/adaptive-templates.d.ts +25 -3
  14. package/dist/installer/adaptive-templates.d.ts.map +1 -1
  15. package/dist/installer/adaptive-templates.js +75 -9
  16. package/dist/installer/adaptive-templates.js.map +1 -1
  17. package/dist/installer/index.d.ts +2 -2
  18. package/dist/installer/index.d.ts.map +1 -1
  19. package/dist/installer/index.js +2 -2
  20. package/dist/installer/index.js.map +1 -1
  21. package/dist/installer/init.d.ts +2 -1
  22. package/dist/installer/init.d.ts.map +1 -1
  23. package/dist/installer/init.js +10 -1
  24. package/dist/installer/init.js.map +1 -1
  25. package/dist/installer/opencode-config.d.ts +4 -3
  26. package/dist/installer/opencode-config.d.ts.map +1 -1
  27. package/dist/installer/opencode-config.js +10 -4
  28. package/dist/installer/opencode-config.js.map +1 -1
  29. package/dist/installer/upgrade.d.ts +5 -0
  30. package/dist/installer/upgrade.d.ts.map +1 -1
  31. package/dist/installer/upgrade.js +44 -10
  32. package/dist/installer/upgrade.js.map +1 -1
  33. package/docs/MIGRATION.md +23 -12
  34. package/docs/SECURITY.md +11 -4
  35. package/docs/TROUBLESHOOTING.md +5 -6
  36. package/package.json +1 -1
  37. package/templates/README.md +5 -2
  38. package/templates/automation/config.json +17 -0
  39. package/templates/automation/config.schema.json +31 -0
  40. package/templates/automation/task-contract.schema.json +22 -1
  41. package/templates/automation/tasks/TASK-TEMPLATE.json.example +4 -1
  42. package/templates/scripts/automation/claim-task.sh +6 -6
  43. package/templates/scripts/automation/lib.sh +118 -0
  44. package/templates/scripts/automation/preflight.sh +1 -1
  45. package/templates/scripts/automation/record-red.sh +7 -9
  46. package/templates/scripts/automation/show-acceptance-review.sh +1 -1
  47. package/templates/scripts/automation/tests/run-tests.sh +26 -9
  48. package/templates/scripts/automation/validate-contract.sh +16 -5
  49. package/templates/scripts/automation/verify-integration.sh +12 -11
  50. package/templates/scripts/automation/verify-task.sh +13 -28
@@ -137,10 +137,18 @@ jq -n '{
137
137
  "systematic-debugging",
138
138
  "verification-before-completion"
139
139
  ],
140
+ gradleVerification: {
141
+ fullUnitTestTasks: ["testDebugUnitTest"],
142
+ focusedTestTasks: ["testDebugUnitTest"],
143
+ assembleTasks: ["assembleDebug"],
144
+ lintTasks: ["lint"],
145
+ deviceTestTasks: ["connectedDebugAndroidTest"]
146
+ },
140
147
  androidProject: {
141
148
  name: "automation-shell-fixture",
142
149
  gradleDsl: "kotlin",
143
150
  settingsFile: "settings.gradle.kts",
151
+ moduleScope: "all",
144
152
  primaryModule: ":mobile-client",
145
153
  modules: [{
146
154
  gradlePath: ":mobile-client",
@@ -217,7 +225,10 @@ jq -n '{
217
225
  ],
218
226
  acceptanceCriteria: ["Greeting returns the approved value"],
219
227
  nonGoals: ["No unrelated refactoring"],
220
- targetTests: ["dev.example.orchestratorfixture.GreetingTest"],
228
+ targetTests: [{
229
+ gradleTask: "testDebugUnitTest",
230
+ filter: "dev.example.orchestratorfixture.GreetingTest"
231
+ }],
221
232
  deviceTestsRequired: false,
222
233
  testPolicy: "required",
223
234
  testPolicyReason: "The behavior requires a focused regression test"
@@ -273,11 +284,17 @@ pass 'illegal state transition is rejected'
273
284
  run_fixture ./scripts/automation/claim-task.sh TASK-TEST-001 >/dev/null
274
285
  [[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-001.json")" == "CODING" ]] || fail 'claim did not create CODING state'
275
286
  [[ -f "$runtime_root/evidence/TASK-TEST-001/baseline.json" ]] || fail 'baseline metadata missing'
287
+ jq -e \
288
+ '.command == ["./gradlew", "testDebugUnitTest"]' \
289
+ "$runtime_root/evidence/TASK-TEST-001/baseline.json" >/dev/null || fail 'baseline command does not match the configured task matrix'
276
290
  pass 'claim verifies workspace identity and records a green baseline'
277
291
 
278
292
  printf '%s\n' 'class GreetingTest { fun expectedBehavior() = Unit }' > "$fixture/mobile-client/src/test/java/dev/example/orchestratorfixture/GreetingTest.kt"
279
293
  run_fixture ./scripts/automation/record-red.sh TASK-TEST-001 'expected missing behavior' -- dev.example.orchestratorfixture.GreetingTest >/dev/null
280
294
  [[ "$(jq -r '.exitCode' "$runtime_root/evidence/TASK-TEST-001/red.json")" -ne 0 ]] || fail 'RED evidence exit code was not captured'
295
+ jq -e \
296
+ '.command == ["./gradlew", "testDebugUnitTest", "--tests", "dev.example.orchestratorfixture.GreetingTest"]' \
297
+ "$runtime_root/evidence/TASK-TEST-001/red.json" >/dev/null || fail 'RED command does not bind the configured task and filter'
281
298
  pass 'focused failing test records genuine RED evidence'
282
299
 
283
300
  printf '%s\n' 'unsafe' > "$fixture/automation/forbidden-change.txt"
@@ -338,7 +355,7 @@ jq \
338
355
  '.id = "TASK-TEST-002" |
339
356
  .title = "Exercise the bounded verification failure loop" |
340
357
  .planPath = "docs/plans/TASK-TEST-002.md" |
341
- .targetTests = ["dev.example.orchestratorfixture.FailureLoopTest"]' \
358
+ .targetTests = [{gradleTask: "testDebugUnitTest", filter: "dev.example.orchestratorfixture.FailureLoopTest"}]' \
342
359
  "$fixture/automation/tasks/TASK-TEST-001.json" \
343
360
  > "$fixture/automation/tasks/TASK-TEST-002.json"
344
361
  (
@@ -375,7 +392,7 @@ jq \
375
392
  '.id = "TASK-TEST-003" |
376
393
  .title = "Verify dirty worktree preflight blocking" |
377
394
  .planPath = "docs/plans/TASK-TEST-003.md" |
378
- .targetTests = ["dev.example.orchestratorfixture.PreflightBlockTest"]' \
395
+ .targetTests = [{gradleTask: "testDebugUnitTest", filter: "dev.example.orchestratorfixture.PreflightBlockTest"}]' \
379
396
  "$fixture/automation/tasks/TASK-TEST-001.json" \
380
397
  > "$fixture/automation/tasks/TASK-TEST-003.json"
381
398
  (
@@ -402,7 +419,7 @@ jq \
402
419
  '.id = "TASK-TEST-005" |
403
420
  .title = "Exercise the bounded reviewer repair cycle" |
404
421
  .planPath = "docs/plans/TASK-TEST-005.md" |
405
- .targetTests = ["dev.example.orchestratorfixture.ReviewerFixTest"]' \
422
+ .targetTests = [{gradleTask: "testDebugUnitTest", filter: "dev.example.orchestratorfixture.ReviewerFixTest"}]' \
406
423
  "$fixture/automation/tasks/TASK-TEST-001.json" \
407
424
  > "$fixture/automation/tasks/TASK-TEST-005.json"
408
425
  (
@@ -452,7 +469,7 @@ jq \
452
469
  '.id = "TASK-TEST-004" |
453
470
  .title = "Exercise in-place transactional integration" |
454
471
  .planPath = "docs/plans/TASK-TEST-004.md" |
455
- .targetTests = ["dev.example.orchestratorfixture.OrchestratedFlowTest"]' \
472
+ .targetTests = [{gradleTask: "testDebugUnitTest", filter: "dev.example.orchestratorfixture.OrchestratedFlowTest"}]' \
456
473
  "$fixture/automation/tasks/TASK-TEST-001.json" \
457
474
  > "$fixture/automation/tasks/TASK-TEST-004.json"
458
475
 
@@ -557,7 +574,7 @@ jq \
557
574
  '.id = "TASK-TEST-008" |
558
575
  .title = "Archive an interrupted in-place task" |
559
576
  .planPath = "docs/plans/TASK-TEST-008.md" |
560
- .targetTests = ["dev.example.orchestratorfixture.AbortArchiveTest"]' \
577
+ .targetTests = [{gradleTask: "testDebugUnitTest", filter: "dev.example.orchestratorfixture.AbortArchiveTest"}]' \
561
578
  "$fixture/automation/tasks/TASK-TEST-001.json" \
562
579
  > "$fixture/automation/tasks/TASK-TEST-008.json"
563
580
  run_fixture ./scripts/automation/prepare-contract-review.sh TASK-TEST-008 '批准方案,生成计划和任务合同。' >/dev/null
@@ -586,7 +603,7 @@ jq \
586
603
  '.id = "TASK-TEST-009" |
587
604
  .title = "Abort before product editing" |
588
605
  .planPath = "docs/plans/TASK-TEST-009.md" |
589
- .targetTests = ["dev.example.orchestratorfixture.PlanningOnlyAbortTest"]' \
606
+ .targetTests = [{gradleTask: "testDebugUnitTest", filter: "dev.example.orchestratorfixture.PlanningOnlyAbortTest"}]' \
590
607
  "$fixture/automation/tasks/TASK-TEST-001.json" \
591
608
  > "$fixture/automation/tasks/TASK-TEST-009.json"
592
609
  run_fixture ./scripts/automation/prepare-contract-review.sh TASK-TEST-009 '批准方案,生成计划和任务合同。' >/dev/null
@@ -611,7 +628,7 @@ jq \
611
628
  '.id = "TASK-TEST-007" |
612
629
  .title = "Keep optional isolated workspace support" |
613
630
  .planPath = "docs/plans/TASK-TEST-007.md" |
614
- .targetTests = ["dev.example.orchestratorfixture.IsolatedFlowTest"]' \
631
+ .targetTests = [{gradleTask: "testDebugUnitTest", filter: "dev.example.orchestratorfixture.IsolatedFlowTest"}]' \
615
632
  "$fixture/automation/tasks/TASK-TEST-001.json" \
616
633
  > "$fixture/automation/tasks/TASK-TEST-007.json"
617
634
  run_fixture ./scripts/automation/prepare-contract-review.sh TASK-TEST-007 '批准方案,生成计划和任务合同。' >/dev/null
@@ -654,7 +671,7 @@ jq \
654
671
  '.id = "TASK-TEST-006" |
655
672
  .title = "Block an advanced original branch" |
656
673
  .planPath = "docs/plans/TASK-TEST-006.md" |
657
- .targetTests = ["dev.example.orchestratorfixture.AdvancedOriginalTest"]' \
674
+ .targetTests = [{gradleTask: "testDebugUnitTest", filter: "dev.example.orchestratorfixture.AdvancedOriginalTest"}]' \
658
675
  "$fixture/automation/tasks/TASK-TEST-001.json" \
659
676
  > "$fixture/automation/tasks/TASK-TEST-006.json"
660
677
  run_fixture ./scripts/automation/prepare-contract-review.sh TASK-TEST-006 '批准方案,生成计划和任务合同。' >/dev/null
@@ -13,6 +13,7 @@ input="${1:-}"
13
13
  [[ -n "$input" ]] || { usage; exit 2; }
14
14
  automation_require_command jq
15
15
  automation_require_layout
16
+ automation_validate_config
16
17
 
17
18
  if [[ "$input" == */* || "$input" == *.json || "$input" == *.example ]]; then
18
19
  contract="$input"
@@ -39,7 +40,12 @@ jq -e '
39
40
  (.allowedSuperpowers == ["test-driven-development", "systematic-debugging", "verification-before-completion"]) and
40
41
  (.acceptanceCriteria | type == "array" and length > 0) and
41
42
  (.nonGoals | type == "array" and length > 0) and
42
- (.targetTests | type == "array" and length > 0) and
43
+ (.targetTests | type == "array" and length > 0 and all(.[];
44
+ type == "object" and
45
+ keys == ["filter", "gradleTask"] and
46
+ (.gradleTask | type == "string" and test("^(?:[A-Za-z][A-Za-z0-9_.-]*|(?::[A-Za-z0-9_.-]+)+)$")) and
47
+ (.filter | type == "string" and test("^[A-Za-z0-9_.#$*-]+$")))) and
48
+ ([.targetTests[].filter] | length == (unique | length)) and
43
49
  (.deviceTestsRequired | type == "boolean") and
44
50
  (.testPolicy == "required" or .testPolicy == "not-required") and
45
51
  (if .testPolicy == "not-required" then (.testPolicyReason | type == "string" and length >= 20) else true end)
@@ -68,10 +74,15 @@ while IFS= read -r protected; do
68
74
  fi
69
75
  done < <(jq -r '.protectedPaths[]' "$AUTOMATION_CONFIG")
70
76
 
71
- safe_filter_pattern='^[A-Za-z0-9_.#$*-]+$'
72
- while IFS= read -r filter; do
73
- [[ "$filter" =~ $safe_filter_pattern ]] || automation_die "unsafe target test filter: $filter"
74
- done < <(jq -r '.targetTests[]' "$contract")
77
+ while IFS=$'\t' read -r gradle_task filter; do
78
+ automation_validate_gradle_task "$gradle_task"
79
+ automation_validate_test_filter "$filter"
80
+ jq -e \
81
+ --arg task "$gradle_task" \
82
+ '.gradleVerification.focusedTestTasks | index($task) != null' \
83
+ "$AUTOMATION_CONFIG" >/dev/null || \
84
+ automation_die "target test Gradle task is not allowed by automation/config.json: $gradle_task"
85
+ done < <(jq -r '.targetTests[] | [.gradleTask, .filter] | @tsv' "$contract")
75
86
 
76
87
  if rg -n -i 'replace with|TASK-EXAMPLE|todo|tbd|placeholder' "$contract" >/dev/null; then
77
88
  automation_die "contract still contains template placeholders"
@@ -12,26 +12,27 @@ head="${3:-}"
12
12
  automation_validate_task_id "$task_id"
13
13
  [[ "$(automation_read_state "$task_id")" == "INTEGRATING" ]] || automation_die "$task_id is not INTEGRATING"
14
14
  contract="$(automation_contract_path "$task_id")"
15
+ "$SCRIPT_DIR/validate-contract.sh" "$task_id" >/dev/null
15
16
 
16
17
  "$SCRIPT_DIR/integration-scope-gate.sh" "$task_id" "$base" "$head"
17
18
 
18
- while IFS= read -r filter; do
19
- automation_info "running integration focused test: $filter"
20
- ./gradlew testDebugUnitTest --tests "$filter"
21
- done < <(jq -r '.targetTests[]' "$contract")
19
+ while IFS=$'\t' read -r gradle_task filter; do
20
+ automation_info "running integration focused test ($gradle_task): $filter"
21
+ automation_run_focused_test "$gradle_task" "$filter" "$AUTOMATION_ROOT"
22
+ done < <(jq -r '.targetTests[] | [.gradleTask, .filter] | @tsv' "$contract")
22
23
 
23
24
  automation_info "running integration full unit tests"
24
- ./gradlew testDebugUnitTest
25
+ automation_run_gradle_group "fullUnitTestTasks" "$AUTOMATION_ROOT"
25
26
 
26
- automation_info "building integration debug APK"
27
- ./gradlew assembleDebug
27
+ automation_info "running configured integration assemble tasks"
28
+ automation_run_gradle_group "assembleTasks" "$AUTOMATION_ROOT"
28
29
 
29
- automation_info "running integration Android lint"
30
- ./gradlew lint
30
+ automation_info "running configured integration Android lint tasks"
31
+ automation_run_gradle_group "lintTasks" "$AUTOMATION_ROOT"
31
32
 
32
33
  if [[ "$(jq -r '.deviceTestsRequired' "$contract")" == "true" ]]; then
33
- automation_info "running integration connected device tests"
34
- ./gradlew connectedDebugAndroidTest
34
+ automation_info "running configured integration device tests"
35
+ automation_run_gradle_group "deviceTestTasks" "$AUTOMATION_ROOT"
35
36
  fi
36
37
 
37
38
  automation_info "integration candidate passed all deterministic verification"
@@ -17,38 +17,23 @@ evidence_dir="$(automation_evidence_path "$task_id")"
17
17
 
18
18
  "$SCRIPT_DIR/scope-gate.sh" "$task_id"
19
19
 
20
- while IFS= read -r filter; do
21
- automation_info "running focused test: $filter"
22
- (
23
- cd "$AUTOMATION_ROOT"
24
- ./gradlew testDebugUnitTest --tests "$filter"
25
- )
26
- done < <(jq -r '.targetTests[]' "$contract")
20
+ while IFS=$'\t' read -r gradle_task filter; do
21
+ automation_info "running focused test ($gradle_task): $filter"
22
+ automation_run_focused_test "$gradle_task" "$filter" "$AUTOMATION_ROOT"
23
+ done < <(jq -r '.targetTests[] | [.gradleTask, .filter] | @tsv' "$contract")
27
24
 
28
25
  automation_info "running full unit tests"
29
- (
30
- cd "$AUTOMATION_ROOT"
31
- ./gradlew testDebugUnitTest
32
- )
33
-
34
- automation_info "building debug APK"
35
- (
36
- cd "$AUTOMATION_ROOT"
37
- ./gradlew assembleDebug
38
- )
39
-
40
- automation_info "running Android lint"
41
- (
42
- cd "$AUTOMATION_ROOT"
43
- ./gradlew lint
44
- )
26
+ automation_run_gradle_group "fullUnitTestTasks" "$AUTOMATION_ROOT"
27
+
28
+ automation_info "running configured assemble tasks"
29
+ automation_run_gradle_group "assembleTasks" "$AUTOMATION_ROOT"
30
+
31
+ automation_info "running configured Android lint tasks"
32
+ automation_run_gradle_group "lintTasks" "$AUTOMATION_ROOT"
45
33
 
46
34
  if [[ "$(jq -r '.deviceTestsRequired' "$contract")" == "true" ]]; then
47
- automation_info "running connected device tests"
48
- (
49
- cd "$AUTOMATION_ROOT"
50
- ./gradlew connectedDebugAndroidTest
51
- )
35
+ automation_info "running configured device tests"
36
+ automation_run_gradle_group "deviceTestTasks" "$AUTOMATION_ROOT"
52
37
  fi
53
38
 
54
39
  automation_info "all deterministic verification commands passed"