@1aboveio/skills 1.0.14 → 1.0.16
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/.codex-plugin/plugin.json +1 -1
- package/.codex-plugin/resources.json +164 -160
- package/package.json +4 -4
- package/skills/app-dev/airflow/SKILL.md +1 -1
- package/skills/app-dev/android-development/SKILL.md +30 -2
- package/skills/app-dev/android-development/references/diagnosis.md +55 -0
- package/skills/app-dev/android-development/references/feature-work.md +15 -0
- package/skills/app-dev/android-development/references/init.md +57 -6
- package/skills/app-dev/android-development/references/setup.md +8 -2
- package/skills/app-dev/android-development/references/ui-journeys.md +58 -0
- package/skills/app-dev/android-development/scripts/device-run.sh +93 -2
- package/skills/app-dev/android-development/scripts/failure_capture.py +331 -0
- package/skills/app-dev/android-development/scripts/init.mjs +234 -17
- package/skills/app-dev/android-development/scripts/maestro_failure.py +401 -0
- package/skills/app-dev/android-development/scripts/test_failure_capture.py +378 -0
- package/skills/app-dev/android-development/scripts/test_maestro_failure.py +462 -0
- package/skills/app-dev/app-debug/SKILL.md +1 -1
- package/skills/app-dev/better-auth/SKILL.md +1 -1
- package/skills/app-dev/data-catalog/SKILL.md +53 -16
- package/skills/app-dev/data-catalog/scripts/persist-conventions-core.mjs +231 -21
- package/skills/app-dev/data-catalog/scripts/persist-conventions.mjs +12 -1
- package/skills/app-dev/data-quality/SKILL.md +1 -1
- package/skills/app-dev/ios-foundation/SKILL.md +1 -1
- package/skills/app-dev/monorepo/SKILL.md +1 -1
- package/skills/app-dev/nextjs-fullstack/SKILL.md +1 -1
- package/skills/app-dev/prisma-setup/SKILL.md +1 -1
- package/skills/app-dev/pyspark/SKILL.md +1 -1
- package/skills/app-dev/python-backend/SKILL.md +1 -1
- package/skills/app-dev/shadcn/SKILL.md +1 -1
- package/skills/app-dev/zod-v4/SKILL.md +1 -1
- package/skills/cicd/cloud-build/SKILL.md +1 -1
- package/skills/cicd/cloud-debug/SKILL.md +1 -1
- package/skills/cicd/cloud-deploy/SKILL.md +1 -1
- package/skills/cicd/docker/SKILL.md +1 -1
- package/skills/cicd/github-actions/SKILL.md +1 -1
- package/skills/cicd/github-flow/SKILL.md +10 -1
- package/skills/cicd/github-flow/scripts/persist-conventions-core.mjs +231 -21
- package/skills/cicd/github-flow/scripts/persist-conventions.mjs +18 -1
- package/skills/cicd/google-cloud/SKILL.md +1 -1
- package/skills/cicd/mergify/SKILL.md +1 -1
- package/skills/cicd/podman/SKILL.md +1 -1
- package/skills/content/article-review/SKILL.md +1 -1
- package/skills/content/article-to-image/SKILL.md +1 -1
- package/skills/content/flowchart-svg/SKILL.md +1 -1
- package/skills/content/image-gen/SKILL.md +1 -1
- package/skills/content/mermaid/SKILL.md +1 -1
- package/skills/content/wechat-article-writer/SKILL.md +1 -1
- package/skills/content/wechat-publisher/SKILL.md +1 -1
- package/skills/content/wechat-search/SKILL.md +1 -1
- package/skills/content/xai-search/SKILL.md +1 -1
- package/skills/core/distill-writing/SKILL.md +7 -1
- package/skills/core/doc-to-md/SKILL.md +1 -1
- package/skills/core/editorial-review/SKILL.md +1 -1
- package/skills/core/house-style/SKILL.md +1 -1
- package/skills/core/house-style/references/persistence.md +8 -3
- package/skills/core/house-style/scripts/init-guidance.mjs +1 -1
- package/skills/core/plain-language/SKILL.md +1 -1
- package/skills/core/polish-cn/SKILL.md +1 -1
- package/skills/core/reg-parser/SKILL.md +1 -1
- package/skills/core/terminology/SKILL.md +1 -1
- package/skills/core/terminology/references/init.md +11 -0
- package/skills/core/write-doc/SKILL.md +1 -1
- package/skills/core/write-report/SKILL.md +1 -1
- package/skills/engineering/better-review/SKILL.md +1 -1
- package/skills/engineering/e2e-test/SKILL.md +1 -1
- package/skills/engineering/engineering/SKILL.md +19 -4
- package/skills/engineering/engineering/references/init-scope.md +5 -2
- package/skills/engineering/engineering/references/scope-management.md +4 -2
- package/skills/engineering/engineering/scripts/init.mjs +34 -9
- package/skills/engineering/engineering/scripts/persist-conventions-core.mjs +231 -21
- package/skills/engineering/engineering/scripts/persist-conventions.mjs +48 -21
- package/skills/engineering/engineering-runtime/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/coherence/workflow.json +13 -13
- package/skills/engineering/ensure-coverage/SKILL.md +1 -1
- package/skills/engineering/golden-path/SKILL.md +1 -1
- package/skills/engineering/harness-runtime/SKILL.md +4 -3
- package/skills/engineering/harness-runtime/agent-catalog.mjs +928 -0
- package/skills/engineering/harness-runtime/contracts.md +2 -0
- package/skills/engineering/harness-runtime/references/model-catalog.md +7 -1
- package/skills/engineering/resolve-issues/SKILL.md +9 -6
- package/skills/engineering/resolve-issues/references/delivery-progress.md +2 -0
- package/skills/engineering/resolve-issues/references/expire.md +6 -3
- package/skills/engineering/resolve-issues/references/implementation.md +6 -1
- package/skills/engineering/resolve-issues/references/preflight.md +26 -17
- package/skills/engineering/resolve-issues/references/review.md +39 -4
- package/skills/engineering/resolve-issues/references/shared-modules.md +88 -20
- package/skills/engineering/resolve-issues/references/task-plan.md +35 -1
- package/skills/engineering/resolve-issues/scripts/preflight-models.mjs +382 -592
- package/skills/engineering/resolve-release/SKILL.md +1 -1
- package/skills/engineering/rush-issues/SKILL.md +4 -4
- package/skills/engineering/rush-release/SKILL.md +1 -1
- package/skills/engineering/test-quality/SKILL.md +3 -1
- package/skills/engineering/test-quality/scripts/persist-conventions-core.mjs +231 -21
- package/skills/engineering/test-quality/scripts/persist-conventions.mjs +13 -1
- package/skills/risk-compliance/compliance-audit/SKILL.md +1 -1
- package/skills/risk-compliance/contract-compare/SKILL.md +1 -1
- package/skills/risk-compliance/contract-review/SKILL.md +1 -1
- package/skills/risk-compliance/doc-authenticity/SKILL.md +1 -1
- package/skills/risk-compliance/fraud-detection/SKILL.md +1 -1
- package/skills/risk-compliance/payment-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/portfolio-payment-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/similar-domain/SKILL.md +1 -1
- package/skills/risk-compliance/tld-detection/SKILL.md +1 -1
- package/skills/engineering/resolve-issues/scripts/discover-models.mjs +0 -9
- package/skills/engineering/resolve-issues/scripts/model-catalog.mjs +0 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1aboveio/skills",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.16",
|
|
4
4
|
"description": "Every 1aboveio skill package in one: install once, then choose which packages Pi loads with npx @1aboveio/skills select.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"nativeCodex": {
|
|
29
29
|
"schemaVersion": 1,
|
|
30
30
|
"metadataPath": ".codex-plugin/resources.json",
|
|
31
|
-
"metadataDigest": "
|
|
32
|
-
"releaseIdentity": "
|
|
31
|
+
"metadataDigest": "efea727f2b53be74c1dad921e4d976e9a4bf22054a5461217bea9a5983de48cc",
|
|
32
|
+
"releaseIdentity": "357a73a8819a5c43f9e6d935ece7e5efc38519d0b83e0984798677c4cb9dfc0d"
|
|
33
33
|
},
|
|
34
34
|
"pi": {
|
|
35
35
|
"skills": [
|
|
@@ -260,7 +260,7 @@
|
|
|
260
260
|
]
|
|
261
261
|
}
|
|
262
262
|
],
|
|
263
|
-
"digest": "
|
|
263
|
+
"digest": "26eb35331224e4dd2e0e8ef13bafd6383af21ae416bcf9e5acc28e68c6b45221"
|
|
264
264
|
},
|
|
265
265
|
"publishConfig": {
|
|
266
266
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: airflow
|
|
3
3
|
description: Airflow DAG architecture and conventions. Use when designing, writing, reviewing, testing, or debugging Airflow DAGs and tasks, including TaskGroups, data intervals, timezone-aware date parameters, CLI validation, environment-based scheduling, and pytest integrity tests.
|
|
4
4
|
user-invocable: true
|
|
5
|
-
release-version: 0.
|
|
5
|
+
release-version: 0.48.0
|
|
6
6
|
---
|
|
7
7
|
**Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
|
|
8
8
|
# Airflow DAG Development Patterns
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: android-development
|
|
3
3
|
description: "Prove and debug Expo / React Native apps on an owned Android emulator. Use for Android startup/rendering failures, Metro or ADB problems, UiAutomator/harness failures, APK builds, SDK/NDK/JDK workspace setup, native journey validation, and choosing Kotlin/Compose versus React Native for Android flows. Reproduce failed Android CI checkpoints off CI before fixing and redispatching. Standalone invocation offers Project or Session; init persists repo conventions. NOT for workflow YAML, CI speed or runners (github-actions), browser tests (e2e-test), iOS (ios-foundation), plain Jest tests, or signing/store release."
|
|
4
|
-
release-version: 0.
|
|
4
|
+
release-version: 0.48.0
|
|
5
5
|
---
|
|
6
6
|
**Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
|
|
7
7
|
# Android development
|
|
@@ -18,7 +18,8 @@ Project rules and required CI remain binding; this skill does not add acceptance
|
|
|
18
18
|
leaves files unchanged. Do not offer global persistence.
|
|
19
19
|
- **Project**, `/android-development init`, or an explicit request to save repo conventions:
|
|
20
20
|
follow [initialization](references/init.md). Preview, then apply the authorized missing
|
|
21
|
-
`CONTRIBUTING.md` section and `AGENTS.md`
|
|
21
|
+
`CONTRIBUTING.md` section and `AGENTS.md` task-entry loader and guide pointer. Check
|
|
22
|
+
the skill loader separately from an existing guide link. No second approval is needed unless
|
|
22
23
|
existing policy conflicts. Initialization is not a policy refresh or delivery permission.
|
|
23
24
|
- **Session:** apply this workflow without writing guidance or agent settings. Retain the
|
|
24
25
|
choice through reload/compaction in this session; a new session makes its own choice.
|
|
@@ -42,6 +43,31 @@ Choose the task, not a fresh environment for every attempt:
|
|
|
42
43
|
repo-specific stages in order and record proof. Do not add Expo or a new harness merely
|
|
43
44
|
to match the examples, or repeat cold setup for ordinary fixes.
|
|
44
45
|
|
|
46
|
+
## Device failure record
|
|
47
|
+
|
|
48
|
+
A failed device case leaves one record and one line, whichever layer saw the failure:
|
|
49
|
+
|
|
50
|
+
- `failure.json` holding `case`, `step`, `error`, `screenshot`, `uiDump`, `logcatTail`,
|
|
51
|
+
`evidenceDir` and `cleanupErrors`, captured at the failing step before teardown.
|
|
52
|
+
- One printed line `NATIVE_FAILURE {"case":…,"step":…,"error":…,"evidenceDir":…}`.
|
|
53
|
+
|
|
54
|
+
Journey code wraps its `wait_for`/`tap` helpers in
|
|
55
|
+
[failure_capture.py](scripts/failure_capture.py), `with recorder.step("tap Pay"):`. Without
|
|
56
|
+
that, [device-run.sh](scripts/device-run.sh) writes a partial record with `step: null` when
|
|
57
|
+
the journey fails and left none. The first failure is never replaced by a cleanup error.
|
|
58
|
+
The contract, other languages and the Maestro/Detox mapping are in
|
|
59
|
+
[diagnosis](references/diagnosis.md#record-the-failed-device-case).
|
|
60
|
+
|
|
61
|
+
## UI journey tool
|
|
62
|
+
|
|
63
|
+
The skill references Maestro; it does not bundle or install it. A repo with no journey tool
|
|
64
|
+
is offered Maestro for new single-device journeys; a repo with a working harness keeps it
|
|
65
|
+
and adds Maestro flows alongside. Run it on the session's own emulator
|
|
66
|
+
(`maestro --device <serial>`) and convert its output with
|
|
67
|
+
[maestro_failure.py](scripts/maestro_failure.py), which writes the record above. Read
|
|
68
|
+
[UI journeys](references/ui-journeys.md) for the device-ownership cost, evidence layout, MCP
|
|
69
|
+
use and the cases that stay custom.
|
|
70
|
+
|
|
45
71
|
## Validation ladder
|
|
46
72
|
|
|
47
73
|
Read [feature work](references/feature-work.md) for selection, ownership and handoff.
|
|
@@ -122,5 +148,7 @@ higher levels still pending rather than claiming acceptance.
|
|
|
122
148
|
| Parallel warm servers and test-first provenance | [Parallel sessions](references/workspace.md#parallel-development-sessions) and [test-first provenance](references/feature-work.md#run-tests-before-chasing-commit-labels) |
|
|
123
149
|
| Builds, provisioning, device execution, cleanup and setup proof | [setup.md](references/setup.md); use [device-run.sh](scripts/device-run.sh) only without an existing resource owner |
|
|
124
150
|
| Feature/fix validation and handoff | [feature-work.md](references/feature-work.md) |
|
|
151
|
+
| Device failure record and evidence capture | [diagnosis.md](references/diagnosis.md#record-the-failed-device-case) |
|
|
152
|
+
| UI journey tool (Maestro) and its evidence converter | [UI journeys](references/ui-journeys.md) |
|
|
125
153
|
| Failure reproduction and correction | [diagnosis.md](references/diagnosis.md), then the relevant [gotcha](references/gotchas.md) |
|
|
126
154
|
| CI integration | [ci.md](references/ci.md), [template](assets/android-ci-job.yml), and `github-actions` |
|
|
@@ -94,6 +94,56 @@ reproduction command, controlled change, observations, evidence locations, prima
|
|
|
94
94
|
cleanup outcomes, and remaining uncertainty. Do not require an APK hash for a compiler
|
|
95
95
|
failure that produced no APK.
|
|
96
96
|
|
|
97
|
+
## Record the failed device case
|
|
98
|
+
|
|
99
|
+
Capture the failing step where it fails, not after cleanup. One contract covers every
|
|
100
|
+
harness, so a coordinator, a fix brief and a later diagnosis read the same thing.
|
|
101
|
+
|
|
102
|
+
`failure.json` in the evidence directory holds `case`, `step`, `error`, `screenshot`,
|
|
103
|
+
`uiDump`, `logcatTail`, `evidenceDir` and `cleanupErrors`. The three artifact fields are
|
|
104
|
+
file names relative to `evidenceDir`, or `null` when that capture failed; the reason then
|
|
105
|
+
goes to `cleanupErrors` and never replaces `error`. Write the record once: the first
|
|
106
|
+
failure is kept, and neither a later failure nor a cleanup problem overwrites it. Print
|
|
107
|
+
exactly one line, `NATIVE_FAILURE {"case":…,"step":…,"error":…,"evidenceDir":…}`, so a
|
|
108
|
+
reader that only sees the run output still learns the cause and where the evidence is.
|
|
109
|
+
|
|
110
|
+
In Python journeys wrap the step helpers in [failure_capture.py](../scripts/failure_capture.py):
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from failure_capture import FailureRecorder
|
|
114
|
+
|
|
115
|
+
recorder = FailureRecorder(case=os.environ["DEVICE_CASE"], evidence_dir=os.environ["LOG_DIR"])
|
|
116
|
+
|
|
117
|
+
with recorder.step("tap Pay"):
|
|
118
|
+
tap("Pay")
|
|
119
|
+
with recorder.step("wait for receipt"):
|
|
120
|
+
wait_for("Thank you")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`recorder.step` is a context manager and a decorator. On the first exception in a step it
|
|
124
|
+
captures `adb exec-out screencap -p`, `uiautomator dump` and a bounded logcat tail on the
|
|
125
|
+
injected serial, writes the record, prints the line and re-raises the original exception.
|
|
126
|
+
Captures are bounded; a capture failure is a `cleanupErrors` entry. A cancelled run
|
|
127
|
+
(`KeyboardInterrupt`, `SystemExit`) is not a failed case.
|
|
128
|
+
|
|
129
|
+
Other languages implement the same two shapes. When the journey harness cannot report a
|
|
130
|
+
step, fix the harness in the PR so every case records one; do not wrap it in a run-only
|
|
131
|
+
script, and do not add a second failure format.
|
|
132
|
+
|
|
133
|
+
Without an in-journey helper, [device-run.sh](../scripts/device-run.sh) falls back: after a
|
|
134
|
+
non-zero journey exit with no `failure.json` in `LOG_DIR` it captures the screen, hierarchy
|
|
135
|
+
and logcat tail before tearing the device down, writes a partial record with `step: null`,
|
|
136
|
+
and prints `NATIVE_FAILURE`. Its `original_stage_exit=<n> cleanup_exit=<n>` line and exit
|
|
137
|
+
status are unchanged, and a record the journey already wrote is left untouched.
|
|
138
|
+
|
|
139
|
+
A repo that already runs Maestro or Detox maps its failing artifacts to this record instead
|
|
140
|
+
of adding the helper: Maestro's failing-step screenshot, view hierarchy and logcat plus its
|
|
141
|
+
`commands.json` step, and Detox's `failing` artifact preset output. For Maestro, run
|
|
142
|
+
[maestro_failure.py](../scripts/maestro_failure.py) over the flow folder or the session folder
|
|
143
|
+
it sits in; it writes the record and prints the line, and [UI journeys](ui-journeys.md#evidence)
|
|
144
|
+
has the evidence layout. A record is written even when the source is the framework's artifact
|
|
145
|
+
set, and the printed line still has the four fields.
|
|
146
|
+
|
|
97
147
|
## Bound work and distinguish progress from waiting
|
|
98
148
|
|
|
99
149
|
Give commands and child waits explicit deadlines appropriate to their purpose. Reserve
|
|
@@ -103,6 +153,11 @@ worker's "running" status does not impose a timeout or demonstrate progress. The
|
|
|
103
153
|
whole journey. Set a positive value for unattended runs; its backward-compatible
|
|
104
154
|
zero default disables that deadline. Allow time for the owner to finish cleanup.
|
|
105
155
|
|
|
156
|
+
Use the harness's background-task mechanism when a journey can exceed the foreground
|
|
157
|
+
command limit. Budget for the selected journey and owned cleanup; feedback-time targets
|
|
158
|
+
are not kill deadlines. Continue independent work and inspect the terminal result when
|
|
159
|
+
notified, rather than waiting or polling in the foreground. Do not add another watcher.
|
|
160
|
+
|
|
106
161
|
For a hung test, identify the actual outstanding command and last completed assertion.
|
|
107
162
|
If the test waits for a child to exit, check whether its input actually requires exit.
|
|
108
163
|
Reduce that question to a bounded host test before launching another emulator.
|
|
@@ -30,6 +30,21 @@ Native/build-input changes invalidate that reuse; unknown compatibility calls fo
|
|
|
30
30
|
rebuild, not an assertion that the old APK is current. Do not run `expo prebuild` routinely
|
|
31
31
|
against tracked native projects.
|
|
32
32
|
|
|
33
|
+
### Check journey admission before reserving devices
|
|
34
|
+
|
|
35
|
+
Before an expensive native journey, verify the exact selected case and supported arguments
|
|
36
|
+
against the existing runner, required installation/resource owners, effective test flags,
|
|
37
|
+
and device settings. Use existing help, parser tests or preflight commands when available;
|
|
38
|
+
do not invent a selector or build another preflight framework. Missing admission support
|
|
39
|
+
is a harness limitation to resolve, not a reason to allocate devices and discover it later.
|
|
40
|
+
Continue independent source tests while device prerequisites are checked.
|
|
41
|
+
|
|
42
|
+
Apply lifecycle-changing display size, density and other required presentation settings
|
|
43
|
+
before app launch when the journey relies on retaining that Activity or observation scope.
|
|
44
|
+
Read back the effective settings through the existing owner. A later setting change may
|
|
45
|
+
recreate the Activity; do not hide scope retirement with registration retries or relaxed
|
|
46
|
+
identity checks. Keep this check scoped to the selected case, not every possible device cell.
|
|
47
|
+
|
|
33
48
|
### Verify command expansion
|
|
34
49
|
|
|
35
50
|
Before adopting a native fast path, trace its package script, wrapper, Gradle tasks and
|
|
@@ -7,15 +7,18 @@ whether anything is persisted.
|
|
|
7
7
|
## Usage
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
|
-
node {baseDir}/scripts/init.mjs [--repo-root <dir>] [--write]
|
|
10
|
+
node {baseDir}/scripts/init.mjs [--repo-root <dir>] [--install-scripts [<dir>]] [--write]
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
- Preview is the default: it prints the planned action per file and writes nothing.
|
|
14
14
|
- `--write` applies the plan. `--repo-root` selects the project; without it the nearest Git root
|
|
15
15
|
from the current directory is used, falling back to the current directory when there is none.
|
|
16
|
+
- `--install-scripts` additionally copies the generic script layer into the repo; see
|
|
17
|
+
[Skill scripts](#skill-scripts-reference-or-copy) for the target and the rules.
|
|
16
18
|
- `--help` prints the usage line.
|
|
17
|
-
- An unknown argument, an unresolvable root, a malformed marker
|
|
18
|
-
non-zero with a message and no
|
|
19
|
+
- An unknown argument, an unresolvable root, a malformed marker, an unsafe target or a file that
|
|
20
|
+
ends inside an unclosed fenced code block or HTML comment exits non-zero with a message and no
|
|
21
|
+
writes, in preview and with `--write` alike.
|
|
19
22
|
|
|
20
23
|
## Files and ownership
|
|
21
24
|
|
|
@@ -27,19 +30,36 @@ to a newly created Android section; report unresolved commands instead of guessi
|
|
|
27
30
|
| File | Write |
|
|
28
31
|
| --- | --- |
|
|
29
32
|
| `CONTRIBUTING.md` | The `## Android development` section, wrapped in `<!-- conventions:android:start -->` / `<!-- conventions:android:end -->`, with `references/android-conventions.md` as its body. |
|
|
30
|
-
| `AGENTS.md` |
|
|
33
|
+
| `AGENTS.md` | An `Android task entry` skill-loading block between `conventions:android-task-entry` markers, plus a pointer to the selected `CONTRIBUTING.md` when missing. Check these independently: a contributor-guide link alone is not a skill loader. |
|
|
34
|
+
| `<dir>/device-run.sh`, `<dir>/failure_capture.py` | Only with `--install-scripts`: the generic layer copied with a version header. |
|
|
31
35
|
|
|
32
36
|
Each file is created only when it is absent. The helper never edits `CLAUDE.md`, never writes into
|
|
33
37
|
a parent project, a home directory or global agent configuration, and never reaches outside the
|
|
34
38
|
selected root.
|
|
35
39
|
|
|
36
40
|
After applying authorized additions, verify that each existing supported agent entrypoint
|
|
37
|
-
reaches the
|
|
41
|
+
reaches both the skill task-entry instruction and the Android contributor-guide section,
|
|
42
|
+
directly or through its instruction chain. A direct link to `CONTRIBUTING.md` does not
|
|
43
|
+
establish that the entrypoint loads the skill rule in `AGENTS.md`. The helper configures
|
|
38
44
|
`AGENTS.md`; it does not certify an independent `CLAUDE.md` loader. Preserve existing chains
|
|
39
45
|
and do not edit that entrypoint under this init permission. If one does not reach the guide,
|
|
40
46
|
report the files configured and the unresolved loading path as partial setup. Obtain separate
|
|
41
47
|
authorization for any additional loader edit instead of silently claiming complete setup.
|
|
42
48
|
|
|
49
|
+
The task-entry block requires reading and applying the installed `android-development`
|
|
50
|
+
skill before Android-specific commands, during issue-unit preflight, in delegated Android
|
|
51
|
+
workers, and again after compaction when its rules are lost. Keep commands and validation
|
|
52
|
+
policy in `CONTRIBUTING.md`, not this loader. An existing direct read/load-before rule or
|
|
53
|
+
managed block is preserved. A managed block is preserved only while it still holds an active
|
|
54
|
+
loader: an empty block, or one whose only loader is commented out or written as a code example,
|
|
55
|
+
is refused rather than counted as configured. A direct `do not load`-style rule that conflicts
|
|
56
|
+
with the loader is refused too, wherever it sits in the file. Fenced code, indented code, HTML
|
|
57
|
+
comments and inline code are documentation, so an example never counts as an active loader or as
|
|
58
|
+
a conflict. An unrecognized `Android task entry` section requires manual reconciliation rather
|
|
59
|
+
than a competing block. Inspect existing rules for conflicts before applying the helper; it
|
|
60
|
+
cannot interpret every natural-language policy. This missing-loader addition is part of explicit
|
|
61
|
+
init even in previously initialized repos, not an automatic update.
|
|
62
|
+
|
|
43
63
|
## Resolve the repo command map
|
|
44
64
|
|
|
45
65
|
The initializer, not the deterministic writer, inspects existing scripts and guidance.
|
|
@@ -81,10 +101,40 @@ Existing guidance wins over the template:
|
|
|
81
101
|
- Refuse a mutation of a multiply hard-linked file or of an alias to the protected
|
|
82
102
|
`CLAUDE.md` entrypoint. This includes a shared AGENTS/CLAUDE target: preserve it and
|
|
83
103
|
report the boundary rather than silently granting a new write permission.
|
|
104
|
+
- Refuse an addition to a file that ends inside an unclosed fenced code block or HTML comment.
|
|
105
|
+
Appended guidance would land inside that construct, be inactive, and be appended again on
|
|
106
|
+
every run. The helper never closes it for you: preserve the manual text, repair the fence or
|
|
107
|
+
comment, and rerun. A file that needs no addition is reported unchanged, so an
|
|
108
|
+
already-configured project behind such a construct is not blocked.
|
|
84
109
|
- An existing pointer is reused only when its link resolves to the selected `CONTRIBUTING.md`,
|
|
85
110
|
and a new pointer is written relative to the real location of `AGENTS.md`. A link to some other
|
|
86
111
|
file, such as a nested package guide, does not count as this project's loader.
|
|
87
112
|
|
|
113
|
+
## Skill scripts: reference or copy
|
|
114
|
+
|
|
115
|
+
`device-run.sh` and `failure_capture.py` are referenced from the installed skill by
|
|
116
|
+
default: nothing is copied, and the run reads them from the skill directory. Copy them
|
|
117
|
+
into the repo only on an explicit request, with `--install-scripts` (default target
|
|
118
|
+
`scripts/android/`) or `--install-scripts <dir>` for another relative directory inside
|
|
119
|
+
the project.
|
|
120
|
+
|
|
121
|
+
A copy carries one header line naming the skill release and the shipped body's hash, for
|
|
122
|
+
example `# android-development skill 0.47.0 sha256:<64 hex> device-run.sh`. The header
|
|
123
|
+
follows a shebang or a module docstring, so neither stops being first; the rest of the file
|
|
124
|
+
is the shipped script byte for byte. That makes the three outcomes unambiguous:
|
|
125
|
+
|
|
126
|
+
- Missing file: `create`. An unmodified copy of the current skill: `unchanged`, so a rerun
|
|
127
|
+
writes nothing. An unmodified copy of an older skill release: `update` re-stamps the
|
|
128
|
+
header and rewrites nothing a caller added.
|
|
129
|
+
- Any other content, including a copy without the header, is `drift`. It is reported in
|
|
130
|
+
preview and with `--write`, the file is preserved, and the run still succeeds. Refresh it
|
|
131
|
+
by hand; initialization never replaces a local edit.
|
|
132
|
+
- Unsafe targets are refused before anything is written: an absolute path, a `..` segment,
|
|
133
|
+
or a directory whose nearest existing ancestor is a file or resolves outside the root.
|
|
134
|
+
|
|
135
|
+
This is the contract's preservation rule, not an update path: a new skill release never
|
|
136
|
+
rewrites project files, and drift is reported rather than resolved.
|
|
137
|
+
|
|
88
138
|
## What it does not do
|
|
89
139
|
|
|
90
140
|
Initialization is not installation or delivery. It does not install dependencies, SDK components
|
|
@@ -92,7 +142,8 @@ or devices, provision an emulator, generate CI, open issues, push or release. It
|
|
|
92
142
|
discover commands automatically: the initializer follows the command map above and reports
|
|
93
143
|
what it could not resolve. Adding verified repository-specific bindings to the new section is
|
|
94
144
|
part of that explicit initialization; a
|
|
95
|
-
conflicting existing rule is reported rather than overwritten.
|
|
145
|
+
conflicting existing rule is reported rather than overwritten. Copying the generic scripts
|
|
146
|
+
does not make the repo's existing harness or CI use them.
|
|
96
147
|
|
|
97
148
|
## Scope and overrides
|
|
98
149
|
|
|
@@ -211,7 +211,12 @@ Pass each command as separate words, not as one quoted string. The script:
|
|
|
211
211
|
uses the separate `JOURNEY_SECONDS` deadline. Leave time for cleanup after that deadline.
|
|
212
212
|
5. Exports `$DB_URL_VAR`, `$DB_NAME_VAR` (`$DB_NAME_PREFIX` plus a unique suffix) and
|
|
213
213
|
`ANDROID_EMULATOR_SERIAL`, then runs the journey.
|
|
214
|
-
6.
|
|
214
|
+
6. On a failing journey that left no record, captures the screenshot, UI dump and logcat
|
|
215
|
+
tail into `LOG_DIR` while the device is still up, writes `failure.json` with `step: null`
|
|
216
|
+
and prints one `NATIVE_FAILURE {...}` line. A record the journey's own helper wrote is
|
|
217
|
+
kept; capture problems are listed in its `cleanupErrors`. See the
|
|
218
|
+
[failure record contract](diagnosis.md#record-the-failed-device-case).
|
|
219
|
+
7. Drops the database, stops the emulator PID (`TERM`, then `KILL` after 10 seconds),
|
|
215
220
|
removes the container by its CID, and prints `original_stage_exit=<n> cleanup_exit=<n>`.
|
|
216
221
|
It exits with the journey's status if the journey failed, else the cleanup status.
|
|
217
222
|
|
|
@@ -220,7 +225,8 @@ group; a wrapper that deliberately creates detached services must stop those ser
|
|
|
220
225
|
through its own owner. Send TERM to the launcher and allow its cleanup to finish rather
|
|
221
226
|
than killing it immediately. The generic runner inherits the caller's ADB endpoint and
|
|
222
227
|
does not stop that server: use a verified private endpoint on a shared host and close it
|
|
223
|
-
through the caller's owner. Capture the command's exit status
|
|
228
|
+
through the caller's owner. Capture the command's exit status, the final summary line and,
|
|
229
|
+
when the journey failed, the `NATIVE_FAILURE` line and its `failure.json`.
|
|
224
230
|
|
|
225
231
|
Expect every selected scenario's `PASS` marker, screenshots, and backend and Metro logs
|
|
226
232
|
when the selected variant uses those services. Preserve the failed checkpoint's evidence
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# UI journey tool: Maestro
|
|
2
|
+
|
|
3
|
+
Maestro writes a single-device UI flow as YAML and records per-step failure evidence. The
|
|
4
|
+
skill references it; it does not bundle or install it, and adding it is the repo's decision.
|
|
5
|
+
|
|
6
|
+
## When to use it
|
|
7
|
+
|
|
8
|
+
| Situation | Recommendation |
|
|
9
|
+
| --- | --- |
|
|
10
|
+
| Repo has no UI journey tool | Offer Maestro for new single-device journeys. It replaces hand-written adb/UiAutomator launch, poll, capture and assertion code. |
|
|
11
|
+
| Repo has a working harness | Keep the harness. Add Maestro flows for new journeys alongside it; do not migrate existing cases. |
|
|
12
|
+
| Coordinated multi-device case | Keep the custom harness. Maestro sharding runs independent flows, and a shard cannot depend on another. |
|
|
13
|
+
|
|
14
|
+
## Device ownership
|
|
15
|
+
|
|
16
|
+
Run against the emulator the session owns and name it, so Maestro never starts or picks its
|
|
17
|
+
own device:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
maestro --device "$ANDROID_SERIAL" test --test-output-dir "$LOG_DIR" ./maestro-flows
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Each session installs and starts Maestro's driver packages (`dev.mobile.maestro`,
|
|
24
|
+
`dev.mobile.maestro.test`) through `am instrument`. That cost lands on the session's first
|
|
25
|
+
flow, not on every step, so run every flow of the case in one session instead of one session
|
|
26
|
+
per flow.
|
|
27
|
+
|
|
28
|
+
## Evidence
|
|
29
|
+
|
|
30
|
+
`--test-output-dir` receives one session folder holding one self-contained folder per flow:
|
|
31
|
+
`manifest.json`, `commands.json` (one entry per executed step), the failing step's
|
|
32
|
+
`screenshots/` image and `screen-hierarchy/` JSON, and `logs/device-logcat.txt`. Convert that
|
|
33
|
+
into the [device failure record](diagnosis.md#record-the-failed-device-case) instead of
|
|
34
|
+
adding the journey helper:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
python3 <skill-base-dir>/scripts/maestro_failure.py --maestro-dir "$LOG_DIR/2026-07-31_173617" \
|
|
38
|
+
--evidence-dir "$LOG_DIR" --case "$DEVICE_CASE"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
It takes the session folder or one flow folder, writes `failure.json` into `--evidence-dir`
|
|
42
|
+
(the flow folder by default) and prints the one `NATIVE_FAILURE` line for the first failed
|
|
43
|
+
step. A passing flow writes nothing, and an existing record is never replaced. The artifact
|
|
44
|
+
fields name the Maestro files (`screenshots/…`, `screen-hierarchy/…`, `logs/…`) rather than
|
|
45
|
+
copying them into the adb names, so the failing frame and its hierarchy stay paired.
|
|
46
|
+
|
|
47
|
+
## Agents
|
|
48
|
+
|
|
49
|
+
`maestro mcp` gives an agent `list_devices`, `inspect_screen`, `take_screenshot` and `run`.
|
|
50
|
+
Explore the screen and draft a flow with it; the committed YAML flow is the proof, not the
|
|
51
|
+
MCP session that found the selectors.
|
|
52
|
+
|
|
53
|
+
## Limits
|
|
54
|
+
|
|
55
|
+
- Coordinated multi-device cases, exact data or payment proofs, and process-death checks stay
|
|
56
|
+
on custom harness code.
|
|
57
|
+
- Authentication codes still come from the harness's disposable database, passed into a flow
|
|
58
|
+
with `maestro test -e` or resolved by its `runScript`; do not add a second fixture path.
|
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
# POSTGRES_IMAGE (postgres:17) DB_URL_VAR (SHARD_ADMIN_URL) DB_NAME_VAR (SHARD_DB_NAME)
|
|
18
18
|
# LOG_DIR (.cache/logs) BOUNDED_SECONDS (30) BOOT_ATTEMPTS (60) KILL_GRACE_SECONDS (10)
|
|
19
19
|
# JOURNEY_SECONDS (0 = no deadline; caller chooses a positive whole-journey limit)
|
|
20
|
+
# DEVICE_CASE (unset) LOGCAT_LINES (400)
|
|
21
|
+
#
|
|
22
|
+
# A journey that fails and left no record of its own gets one before teardown:
|
|
23
|
+
# $LOG_DIR/screenshot.png, $LOG_DIR/ui-dump.xml, $LOG_DIR/logcat-tail.txt and a
|
|
24
|
+
# partial $LOG_DIR/failure.json with step null, plus one `NATIVE_FAILURE {json}`
|
|
25
|
+
# line. A record the journey's own helper wrote is kept, and capture problems go
|
|
26
|
+
# to that record's cleanupErrors instead of replacing the original failure.
|
|
20
27
|
#
|
|
21
28
|
# ADB_SERVER_SOCKET / ANDROID_ADB_SERVER_PORT are inherited unchanged. The caller
|
|
22
29
|
# selects a private ADB endpoint before entry; this script never stops that server.
|
|
@@ -55,6 +62,7 @@ BOUNDED_SECONDS="${BOUNDED_SECONDS:-30}"
|
|
|
55
62
|
BOOT_ATTEMPTS="${BOOT_ATTEMPTS:-60}"
|
|
56
63
|
KILL_GRACE_SECONDS="${KILL_GRACE_SECONDS:-10}"
|
|
57
64
|
JOURNEY_SECONDS="${JOURNEY_SECONDS:-0}"
|
|
65
|
+
LOGCAT_LINES="${LOGCAT_LINES:-400}"
|
|
58
66
|
|
|
59
67
|
# Reject invalid limits before creating any owned resources.
|
|
60
68
|
python3 -c 'import math, sys
|
|
@@ -62,8 +70,9 @@ for value in sys.argv[1:4]:
|
|
|
62
70
|
number = float(value)
|
|
63
71
|
if not math.isfinite(number) or number < 0: sys.exit("limits must be finite and non-negative")
|
|
64
72
|
if float(sys.argv[1]) == 0: sys.exit("BOUNDED_SECONDS must be positive")
|
|
65
|
-
if not sys.argv[4].isdigit() or int(sys.argv[4]) < 1: sys.exit("BOOT_ATTEMPTS must be a positive integer")
|
|
66
|
-
|
|
73
|
+
if not sys.argv[4].isdigit() or int(sys.argv[4]) < 1: sys.exit("BOOT_ATTEMPTS must be a positive integer")
|
|
74
|
+
if not sys.argv[5].isdigit() or int(sys.argv[5]) < 1: sys.exit("LOGCAT_LINES must be a positive integer")' \
|
|
75
|
+
"$BOUNDED_SECONDS" "$KILL_GRACE_SECONDS" "$JOURNEY_SECONDS" "$BOOT_ATTEMPTS" "$LOGCAT_LINES" || exit 2
|
|
67
76
|
for variable in "$DB_URL_VAR" "$DB_NAME_VAR"; do
|
|
68
77
|
if ! [[ "$variable" =~ ^[a-zA-Z_][a-zA-Z_0-9]*$ ]]; then
|
|
69
78
|
echo "invalid database environment variable: $variable" >&2
|
|
@@ -88,6 +97,8 @@ db_ready=""
|
|
|
88
97
|
container_started=""
|
|
89
98
|
launch_in_progress=0
|
|
90
99
|
pending_signal=0
|
|
100
|
+
journey_started=0
|
|
101
|
+
cleanup_errors=()
|
|
91
102
|
handle_signal() {
|
|
92
103
|
pending_signal="$1"
|
|
93
104
|
# Do not abandon a spawned supervisor before its PID has been recorded.
|
|
@@ -174,8 +185,86 @@ managed() {
|
|
|
174
185
|
|
|
175
186
|
bounded() { managed "$BOUNDED_SECONDS" "$@"; }
|
|
176
187
|
|
|
188
|
+
capture_artifact() {
|
|
189
|
+
# capture_artifact <file-name> <adb args...>; an empty capture is not evidence.
|
|
190
|
+
local name="$1" file="$LOG_DIR/$1"
|
|
191
|
+
shift
|
|
192
|
+
if bounded adb -s "$ANDROID_EMULATOR_SERIAL" "$@" > "$file" 2>/dev/null && test -s "$file"; then
|
|
193
|
+
return 0
|
|
194
|
+
fi
|
|
195
|
+
cleanup_errors+=("$name: adb -s $ANDROID_EMULATOR_SERIAL $* produced no evidence")
|
|
196
|
+
rm -f "$file"
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
capture_ui_dump() {
|
|
200
|
+
local remote=/sdcard/android-failure.xml file="$LOG_DIR/ui-dump.xml"
|
|
201
|
+
if bounded adb -s "$ANDROID_EMULATOR_SERIAL" shell uiautomator dump "$remote" >/dev/null 2>&1 \
|
|
202
|
+
&& bounded adb -s "$ANDROID_EMULATOR_SERIAL" exec-out cat "$remote" > "$file" 2>/dev/null \
|
|
203
|
+
&& test -s "$file"; then
|
|
204
|
+
return 0
|
|
205
|
+
fi
|
|
206
|
+
cleanup_errors+=("ui-dump.xml: adb -s $ANDROID_EMULATOR_SERIAL shell uiautomator dump failed")
|
|
207
|
+
rm -f "$file"
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
capture_logcat() {
|
|
211
|
+
# An empty tail is still a capture; a failed one is not.
|
|
212
|
+
local file="$LOG_DIR/logcat-tail.txt"
|
|
213
|
+
if bounded adb -s "$ANDROID_EMULATOR_SERIAL" logcat -d -t "$LOGCAT_LINES" > "$file" 2>/dev/null; then
|
|
214
|
+
return 0
|
|
215
|
+
fi
|
|
216
|
+
cleanup_errors+=("logcat-tail.txt: adb -s $ANDROID_EMULATOR_SERIAL logcat -d -t $LOGCAT_LINES failed")
|
|
217
|
+
rm -f "$file"
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
write_failure_record() {
|
|
221
|
+
local error
|
|
222
|
+
case "$1" in
|
|
223
|
+
124) error="journey exceeded ${JOURNEY_SECONDS}s (status 124): ${journey_cmd[*]}" ;;
|
|
224
|
+
*) error="journey exited with status $1: ${journey_cmd[*]}" ;;
|
|
225
|
+
esac
|
|
226
|
+
# One writer for the record and its line, so their shapes cannot drift.
|
|
227
|
+
python3 - "$LOG_DIR" "${DEVICE_CASE:-}" "$error" \
|
|
228
|
+
${cleanup_errors[@]+"${cleanup_errors[@]}"} >&3 <<'PY' || {
|
|
229
|
+
import json, os, sys
|
|
230
|
+
evidence, case, error = os.path.abspath(sys.argv[1]), sys.argv[2] or None, sys.argv[3]
|
|
231
|
+
record = {"case": case, "step": None, "error": error}
|
|
232
|
+
for key, name in (("screenshot", "screenshot.png"), ("uiDump", "ui-dump.xml"), ("logcatTail", "logcat-tail.txt")):
|
|
233
|
+
path = os.path.join(evidence, name)
|
|
234
|
+
record[key] = name if os.path.isfile(path) and os.path.getsize(path) else None
|
|
235
|
+
record["evidenceDir"] = evidence
|
|
236
|
+
record["cleanupErrors"] = sys.argv[4:]
|
|
237
|
+
path = os.path.join(evidence, "failure.json")
|
|
238
|
+
temporary = path + ".tmp"
|
|
239
|
+
with open(temporary, "w", encoding="utf-8") as stream:
|
|
240
|
+
stream.write(json.dumps(record, indent=2) + "\n")
|
|
241
|
+
os.replace(temporary, path)
|
|
242
|
+
print("NATIVE_FAILURE " + json.dumps(
|
|
243
|
+
{key: record[key] for key in ("case", "step", "error", "evidenceDir")}, separators=(",", ":")))
|
|
244
|
+
PY
|
|
245
|
+
echo "failure record not written; keep the evidence in $LOG_DIR" >&3
|
|
246
|
+
return 0
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
capture_failure() {
|
|
251
|
+
# Called before teardown: the emulator and app are still in the failing state.
|
|
252
|
+
local status="$1" cancelled="$2"
|
|
253
|
+
test "$status" -ne 0 || return 0
|
|
254
|
+
test "$journey_started" -eq 1 || return 0
|
|
255
|
+
# A requested stop is not a failed case.
|
|
256
|
+
test "$cancelled" -eq 0 || return 0
|
|
257
|
+
# The journey's own helper already recorded the failing step; keep that record.
|
|
258
|
+
test ! -e "$LOG_DIR/failure.json" || return 0
|
|
259
|
+
capture_artifact screenshot.png exec-out screencap -p
|
|
260
|
+
capture_ui_dump
|
|
261
|
+
capture_logcat
|
|
262
|
+
write_failure_record "$status"
|
|
263
|
+
}
|
|
264
|
+
|
|
177
265
|
cleanup() {
|
|
178
266
|
original_status=$?
|
|
267
|
+
cancelled_signal="$pending_signal"
|
|
179
268
|
trap - EXIT
|
|
180
269
|
trap '' INT TERM
|
|
181
270
|
pending_signal=0
|
|
@@ -187,6 +276,7 @@ cleanup() {
|
|
|
187
276
|
wait "$active_pid" || :
|
|
188
277
|
active_pid=""
|
|
189
278
|
fi
|
|
279
|
+
capture_failure "$original_status" "$cancelled_signal"
|
|
190
280
|
if test -n "$db_ready"; then
|
|
191
281
|
bounded "${drop_cmd[@]}" || cleanup_status=1
|
|
192
282
|
fi
|
|
@@ -279,4 +369,5 @@ export "$DB_URL_VAR=postgresql://postgres:postgres@127.0.0.1:$db_port/postgres"
|
|
|
279
369
|
export "$DB_NAME_VAR=${DB_NAME_PREFIX}$(date +%s)_$$"
|
|
280
370
|
db_ready=1
|
|
281
371
|
|
|
372
|
+
journey_started=1
|
|
282
373
|
managed "$JOURNEY_SECONDS" "${journey_cmd[@]}"
|