@osfactory/har 0.1.1

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 (71) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +208 -0
  3. package/control/docker-compose.build.yml +7 -0
  4. package/control/docker-compose.yml +31 -0
  5. package/dist/index.js +41753 -0
  6. package/dist/prompts/system-authoring.md +104 -0
  7. package/dist/templates/AGENT.md.template +40 -0
  8. package/dist/templates/CLAUDE.md.template +5 -0
  9. package/dist/templates/adaptation-prompt-init.md +124 -0
  10. package/dist/templates/adaptation-prompt-maintain.md +73 -0
  11. package/dist/templates/cursor-rule.mdc.template +101 -0
  12. package/dist/templates/har-boilerplate/CLAUDE.agent.md +54 -0
  13. package/dist/templates/har-boilerplate/README.md +134 -0
  14. package/dist/templates/har-boilerplate/agent-cli.sh +172 -0
  15. package/dist/templates/har-boilerplate/agent-slot.sh +270 -0
  16. package/dist/templates/har-boilerplate/attach.sh +16 -0
  17. package/dist/templates/har-boilerplate/docker-compose.agent.yml +66 -0
  18. package/dist/templates/har-boilerplate/ecosystem.agent.template.cjs +44 -0
  19. package/dist/templates/har-boilerplate/env.template +37 -0
  20. package/dist/templates/har-boilerplate/harness.env +50 -0
  21. package/dist/templates/har-boilerplate/justfile +39 -0
  22. package/dist/templates/har-boilerplate/launch.sh +229 -0
  23. package/dist/templates/har-boilerplate/setup-infra.sh +102 -0
  24. package/dist/templates/har-boilerplate/stages/README.sh +9 -0
  25. package/dist/templates/har-boilerplate/stages.json +67 -0
  26. package/dist/templates/har-boilerplate/teardown.sh +86 -0
  27. package/dist/templates/har-boilerplate/verify.sh +163 -0
  28. package/dist/templates/har-boilerplate-cli/CLAUDE.agent.md +41 -0
  29. package/dist/templates/har-boilerplate-cli/README.md +112 -0
  30. package/dist/templates/har-boilerplate-cli/agent-cli.sh +162 -0
  31. package/dist/templates/har-boilerplate-cli/agent-slot.sh +270 -0
  32. package/dist/templates/har-boilerplate-cli/docker-compose.agent.yml +64 -0
  33. package/dist/templates/har-boilerplate-cli/harness.env +40 -0
  34. package/dist/templates/har-boilerplate-cli/justfile +27 -0
  35. package/dist/templates/har-boilerplate-cli/launch.sh +132 -0
  36. package/dist/templates/har-boilerplate-cli/setup-infra.sh +102 -0
  37. package/dist/templates/har-boilerplate-cli/stages/README.sh +9 -0
  38. package/dist/templates/har-boilerplate-cli/stages.json +61 -0
  39. package/dist/templates/har-boilerplate-cli/teardown.sh +66 -0
  40. package/dist/templates/har-boilerplate-cli/verify.sh +113 -0
  41. package/dist/templates/har-boilerplate-ios/ADAPT-PROMPT.md +57 -0
  42. package/dist/templates/har-boilerplate-ios/CLAUDE.agent.md +65 -0
  43. package/dist/templates/har-boilerplate-ios/README.md +88 -0
  44. package/dist/templates/har-boilerplate-ios/agent-cli.sh +125 -0
  45. package/dist/templates/har-boilerplate-ios/agent-slot.sh +270 -0
  46. package/dist/templates/har-boilerplate-ios/docker-compose.agent.yml +17 -0
  47. package/dist/templates/har-boilerplate-ios/harness.env +42 -0
  48. package/dist/templates/har-boilerplate-ios/justfile +33 -0
  49. package/dist/templates/har-boilerplate-ios/launch.sh +132 -0
  50. package/dist/templates/har-boilerplate-ios/setup-infra.sh +84 -0
  51. package/dist/templates/har-boilerplate-ios/stages/README.sh +12 -0
  52. package/dist/templates/har-boilerplate-ios/stages.json +61 -0
  53. package/dist/templates/har-boilerplate-ios/teardown.sh +64 -0
  54. package/dist/templates/har-boilerplate-ios/verify.sh +165 -0
  55. package/dist/templates/stage-templates/playwright/.github/workflows/playwright.yml +30 -0
  56. package/dist/templates/stage-templates/playwright/.har/stages/PLAYWRIGHT.md +21 -0
  57. package/dist/templates/stage-templates/playwright/.har/stages/browser-e2e.sh +83 -0
  58. package/dist/templates/stage-templates/playwright/README.md +14 -0
  59. package/dist/templates/stage-templates/playwright/package.fragment.json +14 -0
  60. package/dist/templates/stage-templates/playwright/playwright.config.js +30 -0
  61. package/dist/templates/stage-templates/playwright/template.manifest.json +44 -0
  62. package/dist/templates/stage-templates/playwright/tests/a11y/smoke.spec.js +14 -0
  63. package/dist/templates/stage-templates/playwright/tests/api/health.spec.js +10 -0
  64. package/dist/templates/stage-templates/playwright/tests/frontend/smoke.spec.js +9 -0
  65. package/dist/templates/stage-templates/rocketsim/.har/stages/ROCKETSIM.md +126 -0
  66. package/dist/templates/stage-templates/rocketsim/.har/stages/rocketsim-flows.sh +188 -0
  67. package/dist/templates/stage-templates/rocketsim/README.md +66 -0
  68. package/dist/templates/stage-templates/rocketsim/flows/README.md +36 -0
  69. package/dist/templates/stage-templates/rocketsim/flows/example-smoke.sh +60 -0
  70. package/dist/templates/stage-templates/rocketsim/template.manifest.json +38 -0
  71. package/package.json +78 -0
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env bash
2
+ # Tear down an agent slot for iOS mobile app repos.
3
+ # The session's git branch is KEPT by default so you can push it / open a PR —
4
+ # pass --delete-branch to remove it too.
5
+ #
6
+ # Usage: ./.har/teardown.sh <agent-id> [--delete-branch]
7
+ set -euo pipefail
8
+
9
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
+ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
11
+
12
+ # shellcheck source=/dev/null
13
+ source "$SCRIPT_DIR/harness.env"
14
+ # shellcheck source=/dev/null
15
+ source "$SCRIPT_DIR/agent-slot.sh"
16
+
17
+ AGENT_ID="${1:?Usage: teardown.sh <agent-id> [--delete-branch]}"
18
+ DELETE_BRANCH=false
19
+
20
+ for arg in "${@:2}"; do
21
+ case "$arg" in
22
+ --delete-branch) DELETE_BRANCH=true ;;
23
+ esac
24
+ done
25
+
26
+ validate_agent_id "$AGENT_ID"
27
+
28
+ echo "==> Tearing down agent ${AGENT_ID}..."
29
+
30
+ REGISTRY_FILE="$(slot_registry_file "$AGENT_ID")"
31
+ WORKTREE_PATH=""
32
+ WORK_DIR=""
33
+ BRANCH=""
34
+ if [ -f "$REGISTRY_FILE" ]; then
35
+ WORKTREE_PATH="$(read_slot_field "$REGISTRY_FILE" worktreePath || true)"
36
+ WORK_DIR="$(read_slot_field "$REGISTRY_FILE" workDir || true)"
37
+ BRANCH="$(read_slot_field "$REGISTRY_FILE" branch || true)"
38
+ fi
39
+ [ -n "$WORKTREE_PATH" ] || WORKTREE_PATH="$HOME/worktrees/${HARNESS_PROJECT_NAME}-agent-${AGENT_ID}"
40
+
41
+ rm -f "$REPO_ROOT/.env.agent.${AGENT_ID}"
42
+ if [ -n "$WORK_DIR" ] && [ -d "$WORK_DIR" ]; then
43
+ rm -f "$WORK_DIR/.env.agent.${AGENT_ID}"
44
+ fi
45
+
46
+ if [ -d "$WORKTREE_PATH" ]; then
47
+ rm -f "$WORKTREE_PATH/.env.agent.${AGENT_ID}"
48
+ git -C "$REPO_ROOT" worktree remove "$WORKTREE_PATH" --force 2>/dev/null || rm -rf "$WORKTREE_PATH"
49
+ echo "✓ Removed worktree: $WORKTREE_PATH"
50
+ fi
51
+ git -C "$REPO_ROOT" worktree prune 2>/dev/null || true
52
+
53
+ if [ -n "$BRANCH" ]; then
54
+ if [ "$DELETE_BRANCH" = true ]; then
55
+ git -C "$REPO_ROOT" branch -D "$BRANCH" 2>/dev/null || true
56
+ echo "✓ Deleted branch: $BRANCH"
57
+ else
58
+ echo "✓ Kept branch: $BRANCH (push it or delete with: git branch -D $BRANCH)"
59
+ fi
60
+ fi
61
+
62
+ remove_slot_registry "$AGENT_ID"
63
+
64
+ echo "✓ Agent ${AGENT_ID} torn down"
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env bash
2
+ # Verification pipeline for iOS mobile app repos — build and test with xcodebuild.
3
+ # Outputs JSON to stdout, human-readable progress to stderr.
4
+ #
5
+ # Usage: ./.har/verify.sh <agent-id> [--full]
6
+ #
7
+ # Quick (default): build + unit-tests
8
+ # Full (--full): + lint (SwiftLint) + rocketsim-flows (if installed)
9
+ set -euo pipefail
10
+
11
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
+ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
13
+
14
+ # shellcheck source=/dev/null
15
+ source "$SCRIPT_DIR/harness.env"
16
+ # shellcheck source=/dev/null
17
+ source "$SCRIPT_DIR/agent-slot.sh"
18
+
19
+ AGENT_ID="${1:?Usage: verify.sh <agent-id> [--full]}"
20
+ FULL=""
21
+
22
+ for arg in "${@:2}"; do
23
+ [ "$arg" = "--full" ] && FULL=1
24
+ done
25
+
26
+ validate_agent_id "$AGENT_ID"
27
+
28
+ ENV_FILE="$(resolve_agent_env_file "$AGENT_ID" "$REPO_ROOT")" || {
29
+ echo "No .env.agent.${AGENT_ID} found. Run: ./.har/launch.sh ${AGENT_ID}" >&2
30
+ exit 1
31
+ }
32
+
33
+ set -a
34
+ # shellcheck source=/dev/null
35
+ source "$ENV_FILE"
36
+ set +a
37
+
38
+ WORK_DIR="$(resolve_agent_work_dir "$ENV_FILE")"
39
+
40
+ echo "==> Verifying agent ${AGENT_ID} in ${WORK_DIR}..." >&2
41
+
42
+ # Build xcodebuild project/workspace flags
43
+ xc_target_flags() {
44
+ if [ -n "${HARNESS_XCODE_WORKSPACE:-}" ] && [ -e "$WORK_DIR/${HARNESS_XCODE_WORKSPACE}" ]; then
45
+ echo "-workspace $WORK_DIR/${HARNESS_XCODE_WORKSPACE}"
46
+ elif [ -n "${HARNESS_XCODE_PROJECT:-}" ] && [ -e "$WORK_DIR/${HARNESS_XCODE_PROJECT}" ]; then
47
+ echo "-project $WORK_DIR/${HARNESS_XCODE_PROJECT}"
48
+ else
49
+ # Auto-detect: prefer workspace (CocoaPods), then project
50
+ local ws prj
51
+ ws="$(find "$WORK_DIR" -maxdepth 2 -name "*.xcworkspace" ! -path "*/\.*" 2>/dev/null | head -1 || true)"
52
+ prj="$(find "$WORK_DIR" -maxdepth 2 -name "*.xcodeproj" ! -path "*/\.*" 2>/dev/null | head -1 || true)"
53
+ if [ -n "$ws" ]; then echo "-workspace $ws"
54
+ elif [ -n "$prj" ]; then echo "-project $prj"
55
+ fi
56
+ fi
57
+ }
58
+
59
+ XC_FLAGS="$(xc_target_flags)"
60
+ XC_SCHEME="${HARNESS_XCODE_SCHEME:-MyApp}"
61
+ XC_DESTINATION="${HARNESS_IOS_DESTINATION:-platform=iOS Simulator,name=iPhone 16}"
62
+ XC_DERIVED="${WORK_DIR}/build/DerivedData"
63
+
64
+ OVERALL_PASS=true
65
+ START_TOTAL=$(date +%s%3N 2>/dev/null || echo "0")
66
+ RESULTS_JSON="[]"
67
+
68
+ run_step() {
69
+ local name="$1"
70
+ local cmd="$2"
71
+ local start end elapsed exit_code output
72
+
73
+ printf " → %-40s" "$name..." >&2
74
+ start=$(date +%s%3N 2>/dev/null || echo "0")
75
+
76
+ set +e
77
+ output=$(cd "$WORK_DIR" && eval "$cmd" 2>&1)
78
+ exit_code=$?
79
+ set -e
80
+
81
+ end=$(date +%s%3N 2>/dev/null || echo "0")
82
+ elapsed=$(( end - start ))
83
+
84
+ local pass_bool step_output_escaped
85
+ if [ "$exit_code" = "0" ]; then
86
+ echo "✓ (${elapsed}ms)" >&2
87
+ pass_bool="true"
88
+ else
89
+ echo "✗ (${elapsed}ms)" >&2
90
+ echo "$output" | head -40 | sed 's/^/ /' >&2
91
+ pass_bool="false"
92
+ OVERALL_PASS=false
93
+ fi
94
+
95
+ step_output_escaped=$(echo "$output" | head -50 | \
96
+ node -e "let d=''; process.stdin.on('data',c=>d+=c); process.stdin.on('end',()=>process.stdout.write(JSON.stringify(d.trim())))" \
97
+ 2>/dev/null || echo '""')
98
+
99
+ RESULTS_JSON=$(echo "$RESULTS_JSON" | node -e "
100
+ const fs = require('fs');
101
+ let arr = JSON.parse(fs.readFileSync('/dev/stdin','utf8'));
102
+ arr.push({name:'$name',pass:$pass_bool,ms:$elapsed,output:$step_output_escaped});
103
+ process.stdout.write(JSON.stringify(arr));
104
+ " 2>/dev/null || echo "$RESULTS_JSON")
105
+
106
+ if [ "$pass_bool" = "false" ] && [ -z "$FULL" ]; then
107
+ return 1
108
+ fi
109
+ }
110
+
111
+ # Build (compile-only — catches syntax errors and missing symbols)
112
+ run_step "build" "xcodebuild build \
113
+ ${XC_FLAGS} \
114
+ -scheme \"${XC_SCHEME}\" \
115
+ -destination \"${XC_DESTINATION}\" \
116
+ -derivedDataPath \"${XC_DERIVED}\" \
117
+ CODE_SIGNING_ALLOWED=NO \
118
+ | xcbeautify 2>/dev/null || xcodebuild build \
119
+ ${XC_FLAGS} \
120
+ -scheme \"${XC_SCHEME}\" \
121
+ -destination \"${XC_DESTINATION}\" \
122
+ -derivedDataPath \"${XC_DERIVED}\" \
123
+ CODE_SIGNING_ALLOWED=NO" || { [ -z "$FULL" ] && true; }
124
+
125
+ # Unit tests
126
+ run_step "unit-tests" "xcodebuild test \
127
+ ${XC_FLAGS} \
128
+ -scheme \"${XC_SCHEME}\" \
129
+ -destination \"${XC_DESTINATION}\" \
130
+ -derivedDataPath \"${XC_DERIVED}\" \
131
+ CODE_SIGNING_ALLOWED=NO \
132
+ | xcbeautify 2>/dev/null || xcodebuild test \
133
+ ${XC_FLAGS} \
134
+ -scheme \"${XC_SCHEME}\" \
135
+ -destination \"${XC_DESTINATION}\" \
136
+ -derivedDataPath \"${XC_DERIVED}\" \
137
+ CODE_SIGNING_ALLOWED=NO" || { [ -z "$FULL" ] && true; }
138
+
139
+ if [ -n "$FULL" ]; then
140
+ # SwiftLint — optional, skip gracefully when not installed
141
+ run_step "lint" "command -v \"${HARNESS_SWIFTLINT_CMD:-swiftlint}\" >/dev/null 2>&1 && \
142
+ \"${HARNESS_SWIFTLINT_CMD:-swiftlint}\" --quiet 2>&1 || echo 'swiftlint not installed — skipping'" || true
143
+
144
+ # RocketSim user-flow validation — installed via: har env add-stage rocketsim
145
+ run_rocketsim_flows_if_present "$SCRIPT_DIR" "$AGENT_ID" || true
146
+ fi
147
+
148
+ END_TOTAL=$(date +%s%3N 2>/dev/null || echo "0")
149
+ TOTAL_MS=$(( END_TOTAL - START_TOTAL ))
150
+
151
+ node -e "
152
+ const results = $RESULTS_JSON;
153
+ const overall = results.length > 0 && results.every(r => r.pass);
154
+ const out = {
155
+ status: overall ? 'pass' : 'fail',
156
+ agent_id: $AGENT_ID,
157
+ total_ms: $TOTAL_MS,
158
+ stages: results,
159
+ };
160
+ process.stdout.write(JSON.stringify(out, null, 2) + '\n');
161
+ " 2>/dev/null || echo "{\"status\":\"fail\",\"agent_id\":${AGENT_ID},\"stages\":[]}"
162
+
163
+ if [ "$OVERALL_PASS" = "false" ]; then
164
+ exit 1
165
+ fi
@@ -0,0 +1,30 @@
1
+ name: Playwright tests
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ - name: Setup Node
13
+ uses: actions/setup-node@v4
14
+ with:
15
+ node-version: 18
16
+ - name: Install deps
17
+ run: npm ci
18
+ - name: Install browsers
19
+ run: npx playwright install --with-deps
20
+ - name: Run Playwright tests
21
+ env:
22
+ BASE_URL: ${{ secrets.BASE_URL }}
23
+ TEST_RESET_API: 'true'
24
+ run: npm run test:e2e:ci
25
+ - name: Upload Playwright HTML report
26
+ if: success() || failure()
27
+ uses: actions/upload-artifact@v4
28
+ with:
29
+ name: playwright-report
30
+ path: .har/artifacts/browser-e2e/playwright-report
@@ -0,0 +1,21 @@
1
+ # Playwright (browser-e2e)
2
+
3
+ Adapt specs under `tests/` for your application. Full verification (`verify --full`) runs this stage when `stages/browser-e2e.sh` is present.
4
+
5
+ | Directory | Purpose |
6
+ |-----------|---------|
7
+ | `tests/frontend/` | UI smoke and flows — extend when you add UI |
8
+ | `tests/api/` | HTTP checks via Playwright `request` |
9
+ | `tests/a11y/` | axe-core on key routes |
10
+
11
+ ## Run
12
+
13
+ After `./.har/launch.sh <id>`:
14
+
15
+ ```bash
16
+ ./.har/stages/browser-e2e.sh <id>
17
+ # included in:
18
+ ./.har/verify.sh <id> --full
19
+ ```
20
+
21
+ Replace TODO selectors and paths in the scaffold specs during harness adaptation.
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env bash
2
+ # Playwright browser/API/a11y tests for an agent slot.
3
+ # Outputs JSON to stdout, human-readable progress to stderr.
4
+ #
5
+ # Usage: ./.har/stages/browser-e2e.sh <agent-id>
6
+ # Prerequisite: ./.har/launch.sh <agent-id>
7
+ set -euo pipefail
8
+
9
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
+ HARNESS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
11
+ REPO_ROOT="$(cd "$HARNESS_DIR/.." && pwd)"
12
+
13
+ # shellcheck source=/dev/null
14
+ source "$HARNESS_DIR/harness.env"
15
+ # shellcheck source=/dev/null
16
+ source "$HARNESS_DIR/agent-slot.sh"
17
+
18
+ AGENT_ID="${1:?Usage: browser-e2e.sh <agent-id>}"
19
+ validate_agent_id "$AGENT_ID"
20
+
21
+ log() { echo "==> [browser-e2e agent-$AGENT_ID] $*" >&2; }
22
+
23
+ ENV_FILE="$(resolve_agent_env_file "$AGENT_ID" "$REPO_ROOT")" || {
24
+ echo "No .env.agent.${AGENT_ID} found. Run: ./.har/launch.sh ${AGENT_ID}" >&2
25
+ exit 1
26
+ }
27
+
28
+ set -a
29
+ # shellcheck source=/dev/null
30
+ source "$ENV_FILE"
31
+ set +a
32
+
33
+ WORK_DIR="$(resolve_agent_work_dir "$ENV_FILE")"
34
+ FE_PORT="${FE_PORT:-$(( HARNESS_FE_BASE_PORT + AGENT_ID * 10 ))}"
35
+ API_PORT="${API_PORT:-$(( HARNESS_API_BASE_PORT + AGENT_ID * 10 ))}"
36
+
37
+ export BASE_URL="${BASE_URL:-http://localhost:${FE_PORT}}"
38
+ export API_URL="${API_URL:-http://localhost:${API_PORT}}"
39
+
40
+ ARTIFACT_DIR="$REPO_ROOT/.har/artifacts/browser-e2e"
41
+ mkdir -p "$ARTIFACT_DIR"
42
+
43
+ log "Running Playwright against $BASE_URL (API: $API_URL)"
44
+ log "Work dir: $WORK_DIR"
45
+
46
+ START_TOTAL=$(date +%s%3N 2>/dev/null || echo "0")
47
+
48
+ set +e
49
+ cd "$WORK_DIR"
50
+ PW_OUTPUT=$(npx playwright test 2>&1)
51
+ PW_EXIT=$?
52
+ set -e
53
+
54
+ END_TOTAL=$(date +%s%3N 2>/dev/null || echo "0")
55
+ TOTAL_MS=$(( END_TOTAL - START_TOTAL ))
56
+
57
+ echo "$PW_OUTPUT" >&2
58
+
59
+ REPORT_DIR="$ARTIFACT_DIR/playwright-report"
60
+ REPORT_INDEX="$REPORT_DIR/index.html"
61
+
62
+ node -e "
63
+ const out = {
64
+ status: ${PW_EXIT} === 0 ? 'pass' : 'fail',
65
+ stageId: 'browser-e2e',
66
+ kind: 'test',
67
+ agent_id: ${AGENT_ID},
68
+ total_ms: ${TOTAL_MS},
69
+ urls: [
70
+ { label: 'frontend', url: process.env.BASE_URL || '${BASE_URL}' },
71
+ { label: 'api', url: process.env.API_URL || '${API_URL}' },
72
+ ],
73
+ artifacts: [
74
+ { path: '.har/artifacts/browser-e2e', kind: 'directory' },
75
+ ],
76
+ };
77
+ if (require('fs').existsSync('${REPORT_INDEX}')) {
78
+ out.artifacts.push({ path: '.har/artifacts/browser-e2e/playwright-report', kind: 'report' });
79
+ }
80
+ process.stdout.write(JSON.stringify(out, null, 2) + '\n');
81
+ "
82
+
83
+ exit "$PW_EXIT"
@@ -0,0 +1,14 @@
1
+ # Playwright HAR stage template
2
+
3
+ This template was added by `har env add-stage playwright`. It registers a `browser-e2e` stage and scaffolds a minimal Playwright test suite.
4
+
5
+ ## Next steps
6
+
7
+ ```bash
8
+ npm install
9
+ npx playwright install
10
+ ./.har/launch.sh 1
11
+ ./.har/stages/browser-e2e.sh 1
12
+ ```
13
+
14
+ See `.har/stages/PLAYWRIGHT.md` for adaptation checklist and layout.
@@ -0,0 +1,14 @@
1
+ {
2
+ "scripts": {
3
+ "test:e2e": "playwright test",
4
+ "test:e2e:ui": "playwright test tests/frontend",
5
+ "test:e2e:api": "playwright test tests/api",
6
+ "test:e2e:a11y": "playwright test tests/a11y",
7
+ "test:e2e:headed": "playwright test --headed",
8
+ "test:e2e:ci": "playwright test --reporter=html --workers=2 --retries=2"
9
+ },
10
+ "devDependencies": {
11
+ "@playwright/test": "^1.40.0",
12
+ "axe-core": "^4.7.0"
13
+ }
14
+ }
@@ -0,0 +1,30 @@
1
+ // Playwright config tuned for HAR agent slots and CI.
2
+ const { defineConfig, devices } = require('@playwright/test');
3
+
4
+ module.exports = defineConfig({
5
+ testDir: './tests',
6
+ timeout: 30 * 1000,
7
+ expect: { timeout: 5000 },
8
+ fullyParallel: true,
9
+ forbidOnly: !!process.env.CI,
10
+ retries: process.env.CI ? 2 : 0,
11
+ workers: process.env.CI ? 2 : undefined,
12
+ reporter: [
13
+ ['list'],
14
+ ['html', { open: 'never', outputFolder: '.har/artifacts/browser-e2e/playwright-report' }],
15
+ ],
16
+ outputDir: '.har/artifacts/browser-e2e/test-results',
17
+ use: {
18
+ baseURL: process.env.BASE_URL || 'http://localhost:3000',
19
+ headless: true,
20
+ screenshot: 'only-on-failure',
21
+ video: 'retain-on-failure',
22
+ trace: 'on-first-retry',
23
+ },
24
+ projects: [
25
+ {
26
+ name: 'chromium',
27
+ use: { ...devices['Desktop Chrome'] },
28
+ },
29
+ ],
30
+ });
@@ -0,0 +1,44 @@
1
+ {
2
+ "id": "playwright",
3
+ "stageId": "browser-e2e",
4
+ "verificationStages": [
5
+ "typecheck",
6
+ "unit-tests",
7
+ "api-health",
8
+ "lint",
9
+ "browser-e2e"
10
+ ],
11
+ "stage": {
12
+ "id": "browser-e2e",
13
+ "kind": "test",
14
+ "description": "Playwright E2E (frontend + API + a11y)",
15
+ "script": "stages/browser-e2e.sh",
16
+ "requiresAgentId": true,
17
+ "artifacts": [
18
+ {
19
+ "path": ".har/artifacts/browser-e2e",
20
+ "kind": "directory",
21
+ "description": "HTML report, traces, screenshots, videos"
22
+ },
23
+ {
24
+ "path": ".har/artifacts/browser-e2e/playwright-report",
25
+ "kind": "report"
26
+ }
27
+ ]
28
+ },
29
+ "files": [
30
+ { "src": ".har/stages/browser-e2e.sh", "dest": ".har/stages/browser-e2e.sh", "executable": true },
31
+ { "src": ".har/stages/PLAYWRIGHT.md", "dest": ".har/stages/PLAYWRIGHT.md" },
32
+ { "src": "playwright.config.js", "dest": "playwright.config.js" },
33
+ { "src": "tests/frontend/smoke.spec.js", "dest": "tests/frontend/smoke.spec.js" },
34
+ { "src": "tests/api/health.spec.js", "dest": "tests/api/health.spec.js" },
35
+ { "src": "tests/a11y/smoke.spec.js", "dest": "tests/a11y/smoke.spec.js" },
36
+ { "src": "README.md", "dest": "tests/playwright/README.md" }
37
+ ],
38
+ "optionalFiles": [
39
+ { "src": ".github/workflows/playwright.yml", "dest": ".github/workflows/playwright.yml", "skipFlag": "skipCi" }
40
+ ],
41
+ "merge": {
42
+ "package.json": "package.fragment.json"
43
+ }
44
+ }
@@ -0,0 +1,14 @@
1
+ const { test, expect } = require('@playwright/test');
2
+
3
+ test('homepage passes axe critical/serious checks', async ({ page }) => {
4
+ await page.goto('/');
5
+ await page.addScriptTag({ path: require.resolve('axe-core/axe.min.js') });
6
+ const results = await page.evaluate(async () => {
7
+ // eslint-disable-next-line no-undef
8
+ return await axe.run();
9
+ });
10
+ const blocking = results.violations.filter(
11
+ (v) => v.impact === 'critical' || v.impact === 'serious',
12
+ );
13
+ expect(blocking).toEqual([]);
14
+ });
@@ -0,0 +1,10 @@
1
+ const { test, expect } = require('@playwright/test');
2
+
3
+ // TODO: adapt path and expected body for your app's health endpoint
4
+ test.describe('API smoke', () => {
5
+ test('health endpoint responds', async ({ request }) => {
6
+ const healthPath = process.env.HARNESS_HEALTH_PATH || '/health';
7
+ const res = await request.get(healthPath);
8
+ expect(res.ok()).toBeTruthy();
9
+ });
10
+ });
@@ -0,0 +1,9 @@
1
+ const { test, expect } = require('@playwright/test');
2
+
3
+ // TODO: adapt heading text and routes for your app
4
+ test.describe('Frontend smoke', () => {
5
+ test('homepage loads', async ({ page }) => {
6
+ await page.goto('/');
7
+ await expect(page.locator('body')).toBeVisible();
8
+ });
9
+ });
@@ -0,0 +1,126 @@
1
+ # RocketSim User-Flow Validation
2
+
3
+ The `rocketsim-flows` stage runs reusable bash scripts from `flows/` that interact with your iOS app on the simulator and validate its state. Full verify (`verify --full`) runs all flows automatically.
4
+
5
+ ## Flow script contract
6
+
7
+ A flow is a bash script (`flows/<name>.sh`) that:
8
+
9
+ 1. Uses the `rocketsim` CLI to inspect the UI, interact with the app, and take screenshots
10
+ 2. Exits **0** on success (all assertions passed), **non-zero** on failure
11
+ 3. Writes human-readable progress to **stderr**
12
+ 4. Optionally saves screenshots to `$FLOW_ARTIFACT_DIR` (provided by the runner)
13
+
14
+ The runner sets these environment variables for each flow:
15
+ | Variable | Value |
16
+ |----------|-------|
17
+ | `FLOW_ARTIFACT_DIR` | Directory for screenshots and output of this flow |
18
+ | `WORK_DIR` | Path to the agent's session worktree |
19
+ | `HARNESS_BUNDLE_ID` | App bundle identifier (from `harness.env`) |
20
+ | `HARNESS_SIMULATOR_NAME` | Target simulator name (from `harness.env`) |
21
+ | `AGENT_ID` | Active agent slot id |
22
+
23
+ ## Key RocketSim CLI commands
24
+
25
+ ```bash
26
+ # Health check
27
+ rocketsim doctor
28
+
29
+ # Check which simulator is focused
30
+ rocketsim simulator focused
31
+
32
+ # Read visible UI elements (compact format for agents)
33
+ rocketsim elements --agent
34
+
35
+ # Wait for screen to change after an interaction
36
+ rocketsim wait screen-changed
37
+
38
+ # Wait for a specific element to appear
39
+ rocketsim wait element --label "Continue"
40
+
41
+ # Tap by element label
42
+ rocketsim interact tap --label "Continue"
43
+
44
+ # Tap by type + label (more precise)
45
+ rocketsim interact tap --type Button --label "Submit"
46
+
47
+ # Tap at coordinates
48
+ rocketsim interact tap 210 642
49
+
50
+ # Swipe
51
+ rocketsim interact swipe --direction up
52
+ rocketsim interact swipe --from 200,650 --to 200,150
53
+
54
+ # Type text
55
+ rocketsim interact type "hello@example.com"
56
+
57
+ # Press hardware button
58
+ rocketsim interact button home
59
+
60
+ # Take a screenshot (PNG bytes to stdout)
61
+ rocketsim screenshot > "$FLOW_ARTIFACT_DIR/screen.png"
62
+ ```
63
+
64
+ ## Writing a new flow
65
+
66
+ ```bash
67
+ #!/usr/bin/env bash
68
+ # flows/my-feature.sh — Validate MyFeature after changes
69
+ set -euo pipefail
70
+
71
+ log() { echo "==> [my-feature] $*" >&2; }
72
+
73
+ # 1. Snapshot the current screen
74
+ rocketsim wait screen-changed --timeout 5 2>/dev/null || true
75
+ ELEMENTS=$(rocketsim elements --agent)
76
+
77
+ # 2. Assert expected element is present
78
+ if echo "$ELEMENTS" | grep -q "My Feature Button"; then
79
+ log "✓ My Feature Button visible"
80
+ else
81
+ log "✗ My Feature Button not found"
82
+ rocketsim screenshot > "$FLOW_ARTIFACT_DIR/failure.png"
83
+ exit 1
84
+ fi
85
+
86
+ # 3. Interact
87
+ rocketsim interact tap --label "My Feature Button"
88
+ rocketsim wait screen-changed
89
+
90
+ # 4. Assert outcome
91
+ AFTER=$(rocketsim elements --agent)
92
+ if echo "$AFTER" | grep -q "Expected Result Label"; then
93
+ log "✓ Expected result is visible"
94
+ rocketsim screenshot > "$FLOW_ARTIFACT_DIR/success.png"
95
+ else
96
+ log "✗ Expected result not visible"
97
+ rocketsim screenshot > "$FLOW_ARTIFACT_DIR/failure.png"
98
+ exit 1
99
+ fi
100
+ ```
101
+
102
+ ## Running flows
103
+
104
+ ```bash
105
+ # All flows
106
+ ./.har/stages/rocketsim-flows.sh 1
107
+
108
+ # Single flow
109
+ ./.har/stages/rocketsim-flows.sh 1 example-smoke
110
+
111
+ # Included in full verify
112
+ ./.har/verify.sh 1 --full
113
+ ```
114
+
115
+ Screenshots and per-flow logs are saved under `.har/artifacts/rocketsim-flows/`.
116
+
117
+ ## Guidelines for agents creating flows
118
+
119
+ - **One flow per user journey** — keep flows focused: login, onboarding, settings, a specific feature.
120
+ - **Validate after each action** — use `rocketsim wait screen-changed` or `wait element` before asserting.
121
+ - **Take a screenshot on failure** — always save `$FLOW_ARTIFACT_DIR/failure.png` on assertion failures so humans can diagnose problems.
122
+ - **Take a screenshot on success** — save `$FLOW_ARTIFACT_DIR/success.png` as visual confirmation.
123
+ - **Use label-based selectors** — prefer `--label` over raw coordinates; coordinates break when layout changes.
124
+ - **Reset state** — if the flow navigates away from the start screen, navigate back at the end.
125
+ - **Name flows for their user journey** — `onboarding.sh`, `login.sh`, `settings-notification-toggle.sh`.
126
+ - **Add a new flow for every new UI feature** — this ensures regressions are caught on future changes.