@frankzhang2026/opencode-android-orchestrator 0.1.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 (124) hide show
  1. package/CHANGELOG.md +92 -2
  2. package/LICENSE +21 -0
  3. package/README.md +402 -6
  4. package/THIRD_PARTY_NOTICES.md +119 -0
  5. package/dist/cli.js +360 -1
  6. package/dist/cli.js.map +1 -1
  7. package/dist/compatibility/hooks.d.ts +23 -0
  8. package/dist/compatibility/hooks.d.ts.map +1 -0
  9. package/dist/compatibility/hooks.js +44 -0
  10. package/dist/compatibility/hooks.js.map +1 -0
  11. package/dist/compatibility/versions.d.ts +19 -0
  12. package/dist/compatibility/versions.d.ts.map +1 -1
  13. package/dist/compatibility/versions.js +116 -0
  14. package/dist/compatibility/versions.js.map +1 -1
  15. package/dist/doctor/index.d.ts +36 -0
  16. package/dist/doctor/index.d.ts.map +1 -0
  17. package/dist/doctor/index.js +289 -0
  18. package/dist/doctor/index.js.map +1 -0
  19. package/dist/doctor/installation.d.ts +4 -0
  20. package/dist/doctor/installation.d.ts.map +1 -0
  21. package/dist/doctor/installation.js +497 -0
  22. package/dist/doctor/installation.js.map +1 -0
  23. package/dist/index.d.ts +13 -2
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +13 -2
  26. package/dist/index.js.map +1 -1
  27. package/dist/installer/adaptive-templates.d.ts +78 -0
  28. package/dist/installer/adaptive-templates.d.ts.map +1 -0
  29. package/dist/installer/adaptive-templates.js +233 -0
  30. package/dist/installer/adaptive-templates.js.map +1 -0
  31. package/dist/installer/agents-config.d.ts +19 -0
  32. package/dist/installer/agents-config.d.ts.map +1 -0
  33. package/dist/installer/agents-config.js +147 -0
  34. package/dist/installer/agents-config.js.map +1 -0
  35. package/dist/installer/android-project.d.ts +36 -0
  36. package/dist/installer/android-project.d.ts.map +1 -0
  37. package/dist/installer/android-project.js +418 -0
  38. package/dist/installer/android-project.js.map +1 -0
  39. package/dist/installer/index.d.ts +8 -0
  40. package/dist/installer/index.d.ts.map +1 -1
  41. package/dist/installer/index.js +8 -1
  42. package/dist/installer/index.js.map +1 -1
  43. package/dist/installer/init.d.ts +68 -0
  44. package/dist/installer/init.d.ts.map +1 -0
  45. package/dist/installer/init.js +398 -0
  46. package/dist/installer/init.js.map +1 -0
  47. package/dist/installer/install-manifest.d.ts +132 -0
  48. package/dist/installer/install-manifest.d.ts.map +1 -0
  49. package/dist/installer/install-manifest.js +1047 -0
  50. package/dist/installer/install-manifest.js.map +1 -0
  51. package/dist/installer/opencode-config.d.ts +32 -0
  52. package/dist/installer/opencode-config.d.ts.map +1 -0
  53. package/dist/installer/opencode-config.js +302 -0
  54. package/dist/installer/opencode-config.js.map +1 -0
  55. package/dist/installer/uninstall.d.ts +70 -0
  56. package/dist/installer/uninstall.d.ts.map +1 -0
  57. package/dist/installer/uninstall.js +770 -0
  58. package/dist/installer/uninstall.js.map +1 -0
  59. package/dist/installer/upgrade.d.ts +101 -0
  60. package/dist/installer/upgrade.d.ts.map +1 -0
  61. package/dist/installer/upgrade.js +1140 -0
  62. package/dist/installer/upgrade.js.map +1 -0
  63. package/dist/opencode-plugin.d.ts +2 -0
  64. package/dist/opencode-plugin.d.ts.map +1 -0
  65. package/dist/opencode-plugin.js +4 -0
  66. package/dist/opencode-plugin.js.map +1 -0
  67. package/dist/plugin/index.d.ts +7 -2
  68. package/dist/plugin/index.d.ts.map +1 -1
  69. package/dist/plugin/index.js +23 -6
  70. package/dist/plugin/index.js.map +1 -1
  71. package/dist/tools/index.d.ts +35 -1
  72. package/dist/tools/index.d.ts.map +1 -1
  73. package/dist/tools/index.js +197 -1
  74. package/dist/tools/index.js.map +1 -1
  75. package/docs/MIGRATION.md +184 -0
  76. package/docs/SECURITY.md +219 -0
  77. package/docs/TROUBLESHOOTING.md +171 -0
  78. package/package.json +23 -5
  79. package/templates/.opencode/agents/scheduled-coder.md +115 -0
  80. package/templates/.opencode/agents/scheduled-planner.md +166 -0
  81. package/templates/.opencode/agents/scheduled-reviewer.md +88 -0
  82. package/templates/.opencode/commands/abort-task.md +20 -0
  83. package/templates/.opencode/commands/acceptance.md +20 -0
  84. package/templates/.opencode/commands/change.md +23 -0
  85. package/templates/.opencode/commands/resume-review.md +19 -0
  86. package/templates/.opencode/skills/scheduled-quality-coder/SKILL.md +93 -0
  87. package/templates/.opencode/skills/scheduled-quality-orchestrator/SKILL.md +216 -0
  88. package/templates/.opencode/skills/scheduled-quality-reviewer/SKILL.md +68 -0
  89. package/templates/AGENTS.md.fragment +19 -0
  90. package/templates/README.md +55 -9
  91. package/templates/automation/config.json +67 -0
  92. package/templates/automation/config.schema.json +194 -0
  93. package/templates/automation/task-contract.schema.json +92 -0
  94. package/templates/automation/tasks/TASK-TEMPLATE.json.example +56 -0
  95. package/templates/docs/plans/README.md +26 -0
  96. package/templates/installation-manifest.schema.json +201 -0
  97. package/templates/scripts/automation/abort-task.sh +171 -0
  98. package/templates/scripts/automation/accept-and-integrate.sh +263 -0
  99. package/templates/scripts/automation/acceptance-report.sh +132 -0
  100. package/templates/scripts/automation/approve-and-run.sh +175 -0
  101. package/templates/scripts/automation/begin-review.sh +18 -0
  102. package/templates/scripts/automation/block-task.sh +26 -0
  103. package/templates/scripts/automation/claim-task.sh +74 -0
  104. package/templates/scripts/automation/integration-scope-gate.sh +103 -0
  105. package/templates/scripts/automation/lib.sh +815 -0
  106. package/templates/scripts/automation/orchestrate-task.sh +119 -0
  107. package/templates/scripts/automation/preflight.sh +206 -0
  108. package/templates/scripts/automation/prepare-contract-review.sh +101 -0
  109. package/templates/scripts/automation/quality-gate.sh +72 -0
  110. package/templates/scripts/automation/queue-task.sh +35 -0
  111. package/templates/scripts/automation/record-red.sh +55 -0
  112. package/templates/scripts/automation/resume-review-fix.sh +32 -0
  113. package/templates/scripts/automation/resume-review.sh +120 -0
  114. package/templates/scripts/automation/scope-gate.sh +110 -0
  115. package/templates/scripts/automation/select-task.sh +34 -0
  116. package/templates/scripts/automation/shadow-run.sh +24 -0
  117. package/templates/scripts/automation/show-acceptance-review.sh +111 -0
  118. package/templates/scripts/automation/status.sh +125 -0
  119. package/templates/scripts/automation/submit-review.sh +73 -0
  120. package/templates/scripts/automation/tests/run-tests.sh +716 -0
  121. package/templates/scripts/automation/transition-state.sh +21 -0
  122. package/templates/scripts/automation/validate-contract.sh +98 -0
  123. package/templates/scripts/automation/verify-integration.sh +38 -0
  124. package/templates/scripts/automation/verify-task.sh +39 -0
@@ -0,0 +1,68 @@
1
+ ---
2
+ name: scheduled-quality-reviewer
3
+ description: Use when an orchestrated read-only OpenCode reviewer must independently assess one sealed task in REVIEWING and submit a gated decision
4
+ compatibility: opencode
5
+ metadata:
6
+ audience: automation
7
+ workflow: scheduled-review
8
+ ---
9
+
10
+ # Scheduled quality reviewer
11
+
12
+ Review exactly one task from a fresh session. The coder's summary is a claim,
13
+ not evidence. You have no repository write authority and must not fix the code
14
+ you review.
15
+
16
+ ## Mandatory sequence
17
+
18
+ 1. Load `verification-before-completion`.
19
+ 2. Require exactly one task ID or the compatibility token
20
+ `NEXT_REVIEWING`. For the selector token, first run
21
+ `./scripts/automation/select-task.sh REVIEWING` and continue only if
22
+ exactly one task ID is returned. Then run
23
+ `./scripts/automation/status.sh <TASK-ID>`.
24
+ 3. Continue only when state is `REVIEWING`; the orchestrator alone performs
25
+ the sealed handoff from `READY_FOR_REVIEW`.
26
+ 4. The `status.sh` JSON already includes the approved contract, baseline
27
+ metadata, RED evidence, latest gate metadata, current diff SHA, and the
28
+ sealed-SHA comparison. Use that compact evidence object; do not try to read
29
+ the external shared evidence directory or search generated Gradle report
30
+ directories. Inspect the actual tracked product change with exactly
31
+ `git diff`. The plan and task contract remain uncommitted and are included
32
+ in the sealing scripts' full task SHA even when plain `git diff` does not
33
+ render those untracked files; their hashes are protected, and only product
34
+ paths count against `allowedPaths` and `maxChangedFiles`.
35
+ 5. Check each acceptance criterion against observable behavior. Inspect for
36
+ regression risk, missing edge cases, out-of-scope changes, test deletion,
37
+ ignored tests, relaxed assertions, and implementation-shaped tests.
38
+ 6. Decide independently:
39
+
40
+ - approve only when the diff is correct and evidence is sufficient;
41
+ - request changes for every material finding, with a concrete file/behavior
42
+ explanation.
43
+ 7. Submit one decision as soon as the inspection supports it:
44
+
45
+ `./scripts/automation/submit-review.sh <TASK-ID> APPROVED <summary>`
46
+
47
+ or
48
+
49
+ `./scripts/automation/submit-review.sh <TASK-ID> CHANGES_REQUESTED <summary>`
50
+
51
+ The script reruns the focused tests, full unit suite, debug build, and lint
52
+ before accepting `APPROVED`, and records that fresh output. Therefore do not
53
+ run those Gradle commands separately before an approval submission. Run an
54
+ individual verification command only to diagnose a failed submission, and
55
+ always reserve a step for the final `submit-review.sh` call.
56
+
57
+ ## Independence rules
58
+
59
+ - Never edit code, tests, contracts, agent definitions, skills, scripts, or
60
+ evidence produced by the coder.
61
+ - Never dispatch a reviewer subagent; this scheduled session is the independent
62
+ reviewer.
63
+ - Never approve because the coder says tests passed. The fresh output produced
64
+ by `submit-review.sh` is the authoritative independent verification.
65
+ - Never push, merge, rebase, create a worktree, or move beyond
66
+ `AWAITING_HUMAN`.
67
+ - If verification cannot run, submit `CHANGES_REQUESTED` with the environmental
68
+ blocker. Do not manufacture approval.
@@ -0,0 +1,19 @@
1
+ <!-- opencode-android-orchestrator:begin -->
2
+ ## OpenCode Android Orchestrator
3
+
4
+ Run orchestration commands from the Git repository root.
5
+
6
+ For automated local unit-test verification, run `./gradlew testDebugUnitTest`
7
+ as a standalone command. Do not prepend environment assignments or wrap it in
8
+ another script; this keeps the invocation aligned with the installed OpenCode
9
+ command allowlist.
10
+
11
+ Treat `.opencode/`, `automation/`, `scripts/automation/`, `opencode.json`,
12
+ and this managed block as orchestration infrastructure. Product tasks must not
13
+ modify them unless an explicitly approved task is scoped to maintaining the
14
+ orchestrator itself.
15
+
16
+ Only a fresh OpenCode single-choice `question` selection can grant an
17
+ orchestration approval. Approval-like text in ordinary chat is not approval.
18
+ The orchestrator must not push Git changes or register scheduler/launchd jobs.
19
+ <!-- opencode-android-orchestrator:end -->
@@ -1,14 +1,60 @@
1
1
  # Installation templates
2
2
 
3
- This directory will contain the project-local OpenCode resources extracted
4
- from the verified Android orchestration V3 baseline.
3
+ The project-local OpenCode agents, commands, and skills in `.opencode/` derive
4
+ from the verified Android orchestration V3 baseline at source commit
5
+ `829693652e3737ad94c7cc75214b09fb2b58715b`. Project-dependent agent permissions
6
+ are generalized to Android source sets and remain bounded by each task contract.
5
7
 
6
- Planned template roots:
8
+ Migrated template roots:
7
9
 
8
- - `.opencode/agents`, `.opencode/commands`, and `.opencode/skills`
9
- - `automation` configuration, schemas, and the task contract example
10
- - `scripts/automation` deterministic shell transactions
11
- - `docs/plans/README.md`
10
+ - `.opencode/agents`: `scheduled-planner`, `scheduled-coder`, and
11
+ `scheduled-reviewer`
12
+ - `.opencode/commands`: `change`, `acceptance`, `resume-review`, and
13
+ `abort-task`
14
+ - `.opencode/skills`: the three `scheduled-quality-*` skills
15
+ - `scripts/automation`: all 28 deterministic V3 Bash transactions and their
16
+ test runner, preserved as executable files
17
+ - `automation`: the portable V3 configuration render source, both JSON Schemas,
18
+ and the task contract example
19
+ - `docs/plans/README.md`: the human-approved plan authoring contract
20
+ - `AGENTS.md.fragment`: a bounded managed block for non-destructive
21
+ merging into an existing project `AGENTS.md`
22
+ - `installation-manifest.schema.json`: the portable JSON Schema for installer
23
+ control manifests stored at `.automation-plugin/manifest.json`
12
24
 
13
- Historical tasks, runtime evidence, backups, Android product code, and local
14
- absolute paths must never be included.
25
+ `automation/config.json` is an input to the adaptive renderer, not a directly
26
+ installable configuration. `planAdaptiveProjectTemplates` injects the required
27
+ `androidProject` metadata, repository-relative build files, protected paths,
28
+ all detected production/test source sets, and a module scope. New renders use
29
+ `all` scope and generate task paths for every detected module without requiring
30
+ a primary selection. `primary` scope restricts paths to one module and retains
31
+ the ambiguity guard. The selected/default module still supplies the focused
32
+ test placeholder in both modes.
33
+
34
+ The legacy Scheduler field has been removed while Superpowers remains pinned.
35
+ The scope gates consume generated source-set arrays, and the Shell test fixture
36
+ uses a neutral custom module and package. No shipped template contains a local
37
+ absolute path or project-specific package name.
38
+
39
+ `planAgentsConfigMerge` appends this block without replacing existing project
40
+ instructions, preserves LF/CRLF style, is byte-idempotent, and rejects partial,
41
+ duplicate, or modified managed markers. `init` applies the result through the
42
+ same backup and manifest transaction as every other managed file.
43
+
44
+ Historical tasks, runtime evidence, backups, and Android product code are not
45
+ included.
46
+
47
+ `tests/template-migration.test.mjs` locks the migrated inventory, current
48
+ SHA-256 values, file modes, and portability constraints.
49
+
50
+ `tests/shell-template-migration.test.mjs` additionally locks the Shell file
51
+ inventory, current SHA-256 values, `0755` modes, Bash syntax, portability, and
52
+ no-push/no-publish/no-launchd constraints.
53
+
54
+ `tests/resource-template-migration.test.mjs` locks the five portable
55
+ infrastructure resources, validates their structural alignment, and verifies
56
+ that the AGENTS fragment has exactly one portable managed block.
57
+
58
+ `tests/install-manifest.test.mjs` validates the package-level installation
59
+ manifest Schema together with backup and recovery metadata generated by the
60
+ installer transaction layer.
@@ -0,0 +1,67 @@
1
+ {
2
+ "schemaVersion": 3,
3
+ "enabled": true,
4
+ "mode": "orchestrated",
5
+ "workspaceStrategy": "inPlaceExclusive",
6
+ "originalBranchDriftPolicy": "block",
7
+ "worktreeBase": "",
8
+ "maxFixLoops": 1,
9
+ "maxReviewCycles": 1,
10
+ "maxReviewerRestarts": 2,
11
+ "autoCleanupWorktrees": true,
12
+ "pushAfterAcceptance": false,
13
+ "approvalPhrases": {
14
+ "proposal": "批准方案,生成计划和任务合同。",
15
+ "contract": "合同已复核,批准自动执行到人工验收阶段。",
16
+ "acceptance": "验收通过,提交到原分支。",
17
+ "abort": "中止任务,封存修改并恢复原分支。"
18
+ },
19
+ "plugins": {
20
+ "superpowers": "superpowers@git+https://github.com/obra/superpowers.git#v6.2.0"
21
+ },
22
+ "requiredSkills": [
23
+ "brainstorming",
24
+ "writing-plans",
25
+ "scheduled-quality-orchestrator",
26
+ "scheduled-quality-coder",
27
+ "scheduled-quality-reviewer",
28
+ "test-driven-development",
29
+ "systematic-debugging",
30
+ "verification-before-completion"
31
+ ],
32
+ "gradleVerification": {
33
+ "fullUnitTestTasks": [
34
+ "testDebugUnitTest"
35
+ ],
36
+ "focusedTestTasks": [
37
+ "testDebugUnitTest"
38
+ ],
39
+ "assembleTasks": [
40
+ "assembleDebug"
41
+ ],
42
+ "lintTasks": [
43
+ "lint"
44
+ ],
45
+ "deviceTestTasks": [
46
+ "connectedDebugAndroidTest"
47
+ ]
48
+ },
49
+ "protectedPaths": [
50
+ ".opencode/",
51
+ ".automation-plugin/",
52
+ "automation/",
53
+ "scripts/automation/",
54
+ "opencode.json",
55
+ "opencode.jsonc",
56
+ "AGENTS.md",
57
+ "gradle/",
58
+ "gradlew",
59
+ "gradlew.bat",
60
+ "settings.gradle",
61
+ "settings.gradle.kts",
62
+ "build.gradle",
63
+ "build.gradle.kts",
64
+ "**/build.gradle",
65
+ "**/build.gradle.kts"
66
+ ]
67
+ }
@@ -0,0 +1,194 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:frankzhang2026:opencode-android-orchestrator:automation-config:v3",
4
+ "title": "OpenCode task orchestration configuration",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "$defs": {
8
+ "repositoryPath": {
9
+ "type": "string",
10
+ "minLength": 1,
11
+ "pattern": "^(?!/)(?!\\.\\.(?:/|$))(?![A-Za-z]:[\\\\/]).+"
12
+ },
13
+ "nullableIdentifier": {
14
+ "type": ["string", "null"],
15
+ "minLength": 1
16
+ },
17
+ "gradleTask": {
18
+ "type": "string",
19
+ "minLength": 1,
20
+ "pattern": "^(?:[A-Za-z][A-Za-z0-9_.-]*|(?::[A-Za-z0-9_.-]+)+)$"
21
+ },
22
+ "gradleTaskList": {
23
+ "type": "array",
24
+ "minItems": 1,
25
+ "uniqueItems": true,
26
+ "items": { "$ref": "#/$defs/gradleTask" }
27
+ }
28
+ },
29
+ "required": [
30
+ "schemaVersion",
31
+ "enabled",
32
+ "mode",
33
+ "workspaceStrategy",
34
+ "originalBranchDriftPolicy",
35
+ "worktreeBase",
36
+ "maxFixLoops",
37
+ "maxReviewCycles",
38
+ "maxReviewerRestarts",
39
+ "autoCleanupWorktrees",
40
+ "pushAfterAcceptance",
41
+ "approvalPhrases",
42
+ "plugins",
43
+ "requiredSkills",
44
+ "gradleVerification",
45
+ "androidProject",
46
+ "protectedPaths"
47
+ ],
48
+ "properties": {
49
+ "schemaVersion": { "const": 3 },
50
+ "enabled": { "type": "boolean" },
51
+ "mode": { "enum": ["shadow", "orchestrated"] },
52
+ "workspaceStrategy": {
53
+ "enum": ["inPlaceExclusive", "isolatedWorktree"]
54
+ },
55
+ "originalBranchDriftPolicy": { "const": "block" },
56
+ "worktreeBase": { "type": "string" },
57
+ "maxFixLoops": { "type": "integer", "minimum": 0, "maximum": 1 },
58
+ "maxReviewCycles": { "type": "integer", "minimum": 0, "maximum": 2 },
59
+ "maxReviewerRestarts": { "type": "integer", "minimum": 0, "maximum": 3 },
60
+ "autoCleanupWorktrees": { "type": "boolean" },
61
+ "pushAfterAcceptance": { "const": false },
62
+ "approvalPhrases": {
63
+ "type": "object",
64
+ "additionalProperties": false,
65
+ "required": ["proposal", "contract", "acceptance", "abort"],
66
+ "properties": {
67
+ "proposal": { "type": "string", "minLength": 4 },
68
+ "contract": { "type": "string", "minLength": 4 },
69
+ "acceptance": { "type": "string", "minLength": 4 },
70
+ "abort": { "type": "string", "minLength": 4 }
71
+ }
72
+ },
73
+ "plugins": {
74
+ "type": "object",
75
+ "additionalProperties": false,
76
+ "required": ["superpowers"],
77
+ "properties": {
78
+ "superpowers": { "type": "string", "minLength": 1 }
79
+ }
80
+ },
81
+ "requiredSkills": {
82
+ "type": "array",
83
+ "minItems": 6,
84
+ "uniqueItems": true,
85
+ "items": { "type": "string", "minLength": 1 }
86
+ },
87
+ "gradleVerification": {
88
+ "type": "object",
89
+ "additionalProperties": false,
90
+ "required": [
91
+ "fullUnitTestTasks",
92
+ "focusedTestTasks",
93
+ "assembleTasks",
94
+ "lintTasks",
95
+ "deviceTestTasks"
96
+ ],
97
+ "properties": {
98
+ "fullUnitTestTasks": { "$ref": "#/$defs/gradleTaskList" },
99
+ "focusedTestTasks": { "$ref": "#/$defs/gradleTaskList" },
100
+ "assembleTasks": { "$ref": "#/$defs/gradleTaskList" },
101
+ "lintTasks": { "$ref": "#/$defs/gradleTaskList" },
102
+ "deviceTestTasks": { "$ref": "#/$defs/gradleTaskList" }
103
+ }
104
+ },
105
+ "androidProject": {
106
+ "type": "object",
107
+ "additionalProperties": false,
108
+ "required": [
109
+ "name",
110
+ "gradleDsl",
111
+ "settingsFile",
112
+ "primaryModule",
113
+ "modules",
114
+ "productionPaths",
115
+ "testPaths"
116
+ ],
117
+ "properties": {
118
+ "name": { "type": "string", "minLength": 1 },
119
+ "gradleDsl": { "enum": ["kotlin", "groovy", "mixed"] },
120
+ "settingsFile": { "$ref": "#/$defs/repositoryPath" },
121
+ "moduleScope": { "enum": ["all", "primary"] },
122
+ "primaryModule": {
123
+ "type": "string",
124
+ "pattern": "^:(?:[A-Za-z0-9_.-]+(?::[A-Za-z0-9_.-]+)*)?$"
125
+ },
126
+ "modules": {
127
+ "type": "array",
128
+ "minItems": 1,
129
+ "items": {
130
+ "type": "object",
131
+ "additionalProperties": false,
132
+ "required": [
133
+ "gradlePath",
134
+ "directory",
135
+ "buildFile",
136
+ "dsl",
137
+ "type",
138
+ "namespace",
139
+ "applicationId"
140
+ ],
141
+ "properties": {
142
+ "gradlePath": {
143
+ "type": "string",
144
+ "pattern": "^:(?:[A-Za-z0-9_.-]+(?::[A-Za-z0-9_.-]+)*)?$"
145
+ },
146
+ "directory": { "$ref": "#/$defs/repositoryPath" },
147
+ "buildFile": { "$ref": "#/$defs/repositoryPath" },
148
+ "dsl": { "enum": ["kotlin", "groovy"] },
149
+ "type": {
150
+ "enum": [
151
+ "application",
152
+ "library",
153
+ "dynamic-feature",
154
+ "test",
155
+ "asset-pack"
156
+ ]
157
+ },
158
+ "namespace": { "$ref": "#/$defs/nullableIdentifier" },
159
+ "applicationId": { "$ref": "#/$defs/nullableIdentifier" }
160
+ }
161
+ }
162
+ },
163
+ "productionPaths": {
164
+ "type": "array",
165
+ "minItems": 1,
166
+ "uniqueItems": true,
167
+ "items": {
168
+ "allOf": [
169
+ { "$ref": "#/$defs/repositoryPath" },
170
+ { "pattern": "(^|/)src/main/\\*\\*$" }
171
+ ]
172
+ }
173
+ },
174
+ "testPaths": {
175
+ "type": "array",
176
+ "minItems": 1,
177
+ "uniqueItems": true,
178
+ "items": {
179
+ "allOf": [
180
+ { "$ref": "#/$defs/repositoryPath" },
181
+ { "pattern": "(^|/)src/(?:test|androidTest)/\\*\\*$" }
182
+ ]
183
+ }
184
+ }
185
+ }
186
+ },
187
+ "protectedPaths": {
188
+ "type": "array",
189
+ "minItems": 1,
190
+ "uniqueItems": true,
191
+ "items": { "$ref": "#/$defs/repositoryPath" }
192
+ }
193
+ }
194
+ }
@@ -0,0 +1,92 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:frankzhang2026:opencode-android-orchestrator:task-contract:v1",
4
+ "title": "Scheduled coding task contract",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "$defs": {
8
+ "gradleTask": {
9
+ "type": "string",
10
+ "minLength": 1,
11
+ "pattern": "^(?:[A-Za-z][A-Za-z0-9_.-]*|(?::[A-Za-z0-9_.-]+)+)$"
12
+ },
13
+ "testFilter": {
14
+ "type": "string",
15
+ "minLength": 1,
16
+ "pattern": "^[A-Za-z0-9_.#$*-]+$"
17
+ }
18
+ },
19
+ "required": [
20
+ "schemaVersion",
21
+ "id",
22
+ "title",
23
+ "designApproved",
24
+ "planPath",
25
+ "ambiguityPolicy",
26
+ "maxFixLoops",
27
+ "maxChangedFiles",
28
+ "allowedPaths",
29
+ "forbiddenPaths",
30
+ "allowedSuperpowers",
31
+ "acceptanceCriteria",
32
+ "nonGoals",
33
+ "targetTests",
34
+ "deviceTestsRequired",
35
+ "testPolicy"
36
+ ],
37
+ "properties": {
38
+ "schemaVersion": { "const": 1 },
39
+ "id": { "type": "string", "pattern": "^TASK-[A-Z0-9-]+$" },
40
+ "title": { "type": "string", "minLength": 1 },
41
+ "designApproved": { "const": true },
42
+ "planPath": { "type": "string", "minLength": 1 },
43
+ "ambiguityPolicy": { "const": "BLOCKED" },
44
+ "maxFixLoops": { "type": "integer", "minimum": 0, "maximum": 1 },
45
+ "maxChangedFiles": { "type": "integer", "minimum": 1, "maximum": 12 },
46
+ "allowedPaths": {
47
+ "type": "array",
48
+ "minItems": 1,
49
+ "items": { "type": "string", "minLength": 1 }
50
+ },
51
+ "forbiddenPaths": {
52
+ "type": "array",
53
+ "minItems": 1,
54
+ "items": { "type": "string", "minLength": 1 }
55
+ },
56
+ "allowedSuperpowers": {
57
+ "type": "array",
58
+ "const": [
59
+ "test-driven-development",
60
+ "systematic-debugging",
61
+ "verification-before-completion"
62
+ ]
63
+ },
64
+ "acceptanceCriteria": {
65
+ "type": "array",
66
+ "minItems": 1,
67
+ "items": { "type": "string", "minLength": 1 }
68
+ },
69
+ "nonGoals": {
70
+ "type": "array",
71
+ "minItems": 1,
72
+ "items": { "type": "string", "minLength": 1 }
73
+ },
74
+ "targetTests": {
75
+ "type": "array",
76
+ "minItems": 1,
77
+ "uniqueItems": true,
78
+ "items": {
79
+ "type": "object",
80
+ "additionalProperties": false,
81
+ "required": ["gradleTask", "filter"],
82
+ "properties": {
83
+ "gradleTask": { "$ref": "#/$defs/gradleTask" },
84
+ "filter": { "$ref": "#/$defs/testFilter" }
85
+ }
86
+ }
87
+ },
88
+ "deviceTestsRequired": { "type": "boolean" },
89
+ "testPolicy": { "enum": ["required", "not-required"] },
90
+ "testPolicyReason": { "type": "string" }
91
+ }
92
+ }
@@ -0,0 +1,56 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "TASK-EXAMPLE-001",
4
+ "title": "Replace with one small, observable behavior change",
5
+ "designApproved": true,
6
+ "planPath": "docs/plans/TASK-EXAMPLE-001.md",
7
+ "ambiguityPolicy": "BLOCKED",
8
+ "maxFixLoops": 1,
9
+ "maxChangedFiles": 4,
10
+ "allowedPaths": [
11
+ "**/src/main/**",
12
+ "**/src/test/**",
13
+ "**/src/androidTest/**"
14
+ ],
15
+ "forbiddenPaths": [
16
+ ".opencode/**",
17
+ ".automation-plugin/**",
18
+ "automation/**",
19
+ "scripts/automation/**",
20
+ "opencode.json",
21
+ "opencode.jsonc",
22
+ "AGENTS.md",
23
+ "gradle/**",
24
+ "gradlew",
25
+ "gradlew.bat",
26
+ "settings.gradle",
27
+ "settings.gradle.kts",
28
+ "build.gradle",
29
+ "build.gradle.kts",
30
+ "**/build.gradle",
31
+ "**/build.gradle.kts"
32
+ ],
33
+ "allowedSuperpowers": [
34
+ "test-driven-development",
35
+ "systematic-debugging",
36
+ "verification-before-completion"
37
+ ],
38
+ "acceptanceCriteria": [
39
+ "Replace with an observable input/output or UI behavior",
40
+ "A focused test demonstrates the behavior and is captured RED before implementation",
41
+ "Unit tests, debug build, and lint all pass"
42
+ ],
43
+ "nonGoals": [
44
+ "Do not refactor unrelated screens",
45
+ "Do not upgrade dependencies or Gradle configuration"
46
+ ],
47
+ "targetTests": [
48
+ {
49
+ "gradleTask": "testDebugUnitTest",
50
+ "filter": "*ReplaceWithFocusedTest"
51
+ }
52
+ ],
53
+ "deviceTestsRequired": false,
54
+ "testPolicy": "required",
55
+ "testPolicyReason": "Behavior changes require a focused regression test"
56
+ }
@@ -0,0 +1,26 @@
1
+ # Human-approved automation plans
2
+
3
+ Every orchestrated coding contract must reference one plan in this directory.
4
+ Planning is interactive and happens before a task is approved for execution; unattended agents
5
+ must not create, broaden, or reinterpret these plans.
6
+
7
+ The normal entry point is a natural-language task description entered in an
8
+ interactive `scheduled-planner` OpenCode session. The planner must inspect the
9
+ repository, present the proposed scope and acceptance criteria, and receive
10
+ explicit human approval before it creates this plan and the matching JSON
11
+ contract.
12
+
13
+ A plan should contain:
14
+
15
+ - one observable behavior change;
16
+ - acceptance criteria and edge cases;
17
+ - allowed and forbidden repository paths;
18
+ - the focused test class/filter that will provide RED evidence;
19
+ - explicit non-goals;
20
+ - device/emulator requirements, if any;
21
+ - a dated human approval statement.
22
+
23
+ After proposal approval, Planner seals both artifacts in `CONTRACT_REVIEW`.
24
+ The matching contract belongs in `automation/tasks/TASK-<ID>.json`. A missing
25
+ plan, placeholder text, or mismatched task ID causes contract validation to
26
+ fail.