@aarwitz/tapp 0.16.0 → 0.16.2

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/README.md CHANGED
@@ -35,34 +35,22 @@ agent: *tapp: builds, opens the app, navigates to Settings, screenshots it*
35
35
  agent: "Done — and here it is working on the simulator: [screenshot]"
36
36
  ```
37
37
 
38
- ## Quickstart
38
+ ## Quickstart for coding agents
39
39
 
40
40
  Requirements: **Node ≥ 18**. iOS needs **macOS + Xcode**; Android needs `adb` plus a connected
41
41
  emulator/device; web needs Playwright + Chromium.
42
42
 
43
- **Launch experience browser Release Studio.** Open the source chooser from anywhere:
43
+ From the app repository, let the agent see the current screen and then ask for a release verdict:
44
44
 
45
45
  ```bash
46
- npx -y @aarwitz/tapp app
46
+ npx -y @aarwitz/tapp open # builds/launches as needed; prints a screenshot path + screen summary
47
+ npx -y @aarwitz/tapp qa # explores the real app; prints verdict, findings, and evidence report
47
48
  ```
48
49
 
49
- Drag and drop a local repository folder or connect through your authenticated GitHub CLI and select
50
- an authorized repository. To work directly in an existing writable checkout, use `tapp app .`.
51
- The local, loopback-only workspace detects iOS, Android, and web targets. A single configured target
52
- builds and explores automatically; Tapp asks only when selection is ambiguous or configuration is
53
- genuinely missing. It renders the UI Map,
54
- supports release-contract review and keyless validation, records live semantic actions as committed
55
- Flows, and produces target-scoped gate, baseline, evidence, and CI artifacts. CLI, MCP, VS Code, and
56
- the GitHub Action use the same product/gate operations; the retained managed-runner prototype is
57
- being replaced by the future isolated SaaS worker boundary.
58
-
59
- **Zero config — get a verdict right now.** From your app's repo, one command. No server, no
60
- config file, no test code — you don't even need to know your bundle id:
61
-
62
- ```bash
63
- cd YourApp
64
- npx -y @aarwitz/tapp qa # finds your Xcode project → builds → installs on the simulator → explores → verdict
65
- ```
50
+ Claude Code can read the saved image with its file-reading tool; Codex can open it with
51
+ `view_image`. The agent should report what the screenshot proves, preserve Tapp's exact
52
+ `ready`/`caution`/`blocked` verdict, and link the HTML evidence report. No server, account, config
53
+ file, test code, API key, or bundle id is required for this loop.
66
54
 
67
55
  The product, executable, and package leaf are all Tapp: npm distributes it as
68
56
  `@aarwitz/tapp`, while the installed command remains `tapp`.
@@ -124,6 +112,12 @@ npx -y @aarwitz/tapp install # ~2 min, one time
124
112
  npx -y @aarwitz/tapp doctor # verify Xcode / simulators / toolchain
125
113
  ```
126
114
 
115
+ ### Optional browser workspace
116
+
117
+ `npx -y @aarwitz/tapp app .` opens a local Release Studio for people who want visual repository
118
+ onboarding, release-plan review, and CI preparation. It is not required for the coding-agent
119
+ `open`/`qa` workflow.
120
+
127
121
  ### MCP hookup (optional)
128
122
 
129
123
  The MCP server adds the two things a CLI can't do: **screenshots inline in your agent's
package/bin/tapp.js CHANGED
@@ -461,6 +461,27 @@ switch (command) {
461
461
  const { flags, positionals } = parseVerbArgs(rest);
462
462
  const engine = await engineImport();
463
463
  const platform = requestedPlatform(flags, positionals[0] || "");
464
+ if (platform === "web") {
465
+ const url = positionals[0] || "";
466
+ if (!/^https?:\/\//i.test(url)) {
467
+ console.error("❌ Web open needs an http(s) URL");
468
+ process.exit(2);
469
+ }
470
+ try {
471
+ const { inspectWebPage } = await import(path.join(packageRoot, "mcp-server", "src", "web-explorer.js"));
472
+ const snap = await inspectWebPage({ url, timeoutMs: Number(flags.timeout) * 1000 || 15_000 });
473
+ const out = typeof flags.out === "string" ? path.resolve(flags.out) : path.join(tappHome, "shots", `web-${Date.now()}.png`);
474
+ fs.mkdirSync(path.dirname(out), { recursive: true });
475
+ fs.writeFileSync(out, snap.image);
476
+ console.log(`🌐 Opened \`${snap.url}\`\n`);
477
+ console.log(engine.formatScreen(snap.screenTitle, snap.elements));
478
+ console.log(`\n📸 Screenshot: ${out}`);
479
+ } catch (error) {
480
+ console.error(`❌ ${error.message || String(error)}`);
481
+ process.exit(1);
482
+ }
483
+ break;
484
+ }
464
485
  if (platform === "android") {
465
486
  const target = androidTarget(flags, positionals[0] || "");
466
487
  const { AndroidDriver } = await import(path.join(packageRoot, "mcp-server", "src", "android-driver.js"));
@@ -504,6 +525,23 @@ switch (command) {
504
525
  const { flags, positionals } = parseVerbArgs(rest);
505
526
  const engine = await engineImport();
506
527
  const platform = requestedPlatform(flags, positionals[0] || "");
528
+ if (platform === "web") {
529
+ const url = positionals[0] || "";
530
+ if (!/^https?:\/\//i.test(url)) {
531
+ console.error("❌ Web tree needs an http(s) URL");
532
+ process.exit(2);
533
+ }
534
+ try {
535
+ const { inspectWebPage } = await import(path.join(packageRoot, "mcp-server", "src", "web-explorer.js"));
536
+ const snap = await inspectWebPage({ url, timeoutMs: Number(flags.timeout) * 1000 || 15_000, screenshot: false });
537
+ if (flags.json) console.log(JSON.stringify({ platform: "web", url: snap.url, screenTitle: snap.screenTitle, elements: snap.elements }, null, 2));
538
+ else console.log(engine.formatScreen(snap.screenTitle, snap.elements));
539
+ } catch (error) {
540
+ console.error(`❌ ${error.message || String(error)}`);
541
+ process.exit(1);
542
+ }
543
+ break;
544
+ }
507
545
  if (platform === "android") {
508
546
  const target = androidTarget(flags, positionals[0] || "");
509
547
  const { AndroidDriver } = await import(path.join(packageRoot, "mcp-server", "src", "android-driver.js"));
@@ -1008,7 +1046,9 @@ switch (command) {
1008
1046
  }
1009
1047
 
1010
1048
  console.log(`\n Home: ${tappHome}`);
1011
- console.log(healthy ? "\nReady. Add to your agent: claude mcp add tapp -- npx -y @aarwitz/tapp mcp" : "\nFix the ❌ items above, then re-run: tapp doctor");
1049
+ console.log(healthy
1050
+ ? "\nReady. Start with:\n npx -y @aarwitz/tapp open [target]\n npx -y @aarwitz/tapp qa [target]"
1051
+ : "\nFix the ❌ items above, then re-run: tapp doctor");
1012
1052
  process.exit(healthy ? 0 : 1);
1013
1053
  }
1014
1054
 
@@ -1317,21 +1357,9 @@ switch (command) {
1317
1357
  console.log(`tapp v${pkg.version} — ship with proof. Autonomous QA and deterministic Flows for iOS, Android, and web.
1318
1358
 
1319
1359
  Zero-config verbs (agents and humans can just run these — no server, no setup):
1320
- tapp app [repo] Open the browser onboarding, contract-review, and release-evidence workspace
1321
- (loopback-only; --no-open · --port PORT)
1322
- tapp init [repo] Detect targets and write the application model + reviewable release plan
1323
- (--explore builds/starts or connects, grounds the UI Map, then tears down)
1324
- (--url URL · --platform PLATFORM · --dry-run · --refresh)
1325
- tapp actor list [repo] Inspect named actors, sessions, provisioning, and secret env bindings
1326
- tapp actor set NAME Configure an actor using environment-variable names only (never values)
1327
- tapp plan show [FILE] Inspect the proposed/accepted release-contract plan
1328
- tapp plan review [FILE] Explicitly approve, reject, or defer proposed plan items
1329
- tapp plan generate [FILE] Generate compile-checked, untrusted contract drafts from approved Tasks
1330
- tapp plan validate [FILE] Replay drafts on a real target; trust only after all platforms pass
1331
- tapp plan promote [FILE] Move fully validated drafts into reviewed Tasks/contracts + map coverage
1360
+ tapp open [target] Launch the app screen summary + screenshot saved to a file
1332
1361
  tapp qa [target] Autonomous QA → verdict + findings + evidence
1333
1362
  (--platform ios|android|web · --app-id ID · --apk FILE · --actions N)
1334
- tapp open [target] Launch the app → screen summary + screenshot saved to a file
1335
1363
  tapp tree [target] Accessibility tree of the current screen (--json for every element)
1336
1364
  tapp flow run FILE Replay a committed deterministic Flow (no AI/API key)
1337
1365
  tapp flow validate FILE Validate a Flow without launching a target
@@ -1353,6 +1381,18 @@ Zero-config verbs (agents and humans can just run these — no server, no setup)
1353
1381
  tapp build [dir] Build the iOS app in a repo for the simulator + install it (--scheme S)
1354
1382
  tapp apps List apps installed on the booted simulator (with bundle ids)
1355
1383
  tapp report [captureId] Open the HTML evidence page for a capture (default: latest)
1384
+ tapp app [repo] Optional local browser workspace for repository onboarding and review
1385
+ (loopback-only; --no-open · --port PORT)
1386
+ tapp init [repo] Detect targets and write the application model + reviewable release plan
1387
+ (--explore builds/starts or connects, grounds the UI Map, then tears down)
1388
+ (--url URL · --platform PLATFORM · --dry-run · --refresh)
1389
+ tapp actor list [repo] Inspect named actors, sessions, provisioning, and secret env bindings
1390
+ tapp actor set NAME Configure an actor using environment-variable names only (never values)
1391
+ tapp plan show [FILE] Inspect the proposed/accepted release-contract plan
1392
+ tapp plan review [FILE] Explicitly approve, reject, or defer proposed plan items
1393
+ tapp plan generate [FILE] Generate compile-checked, untrusted contract drafts from approved Tasks
1394
+ tapp plan validate [FILE] Replay drafts on a real target; trust only after all platforms pass
1395
+ tapp plan promote [FILE] Move fully validated drafts into reviewed Tasks/contracts + map coverage
1356
1396
  tapp ci ... Merge-blocking release gate — explore + flows + baseline diff (see: tapp ci --help)
1357
1397
  tapp ci install [repo] Generate a reviewable target-aware GitHub workflow + CI manifest
1358
1398
 
@@ -97,6 +97,65 @@ export function webBrowserLaunchOptions(environment = process.env) {
97
97
  };
98
98
  }
99
99
 
100
+ // Focused one-screen inspection for the agent-facing `tapp open <url>` and `tapp tree <url>`
101
+ // commands. This deliberately does no exploration or judgment; it opens exactly one page,
102
+ // captures the visible semantic controls, and optionally takes one screenshot.
103
+ export async function inspectWebPage({ url, timeoutMs = NAV_TIMEOUT_MS, screenshot = true }) {
104
+ let target;
105
+ try { target = new URL(url); }
106
+ catch { throw new Error("Web inspection needs a valid http(s) URL"); }
107
+ if (!/^https?:$/.test(target.protocol)) throw new Error("Web inspection needs a valid http(s) URL");
108
+
109
+ const { chromium } = await loadPlaywright();
110
+ const browser = await chromium.launch(webBrowserLaunchOptions());
111
+ try {
112
+ const context = await browser.newContext({ viewport: { width: 1280, height: 900 } });
113
+ const page = await context.newPage();
114
+ const boundedTimeout = Math.max(1000, Math.min(60_000, Number(timeoutMs) || NAV_TIMEOUT_MS));
115
+ page.setDefaultTimeout(boundedTimeout);
116
+ const response = await page.goto(target.href, { waitUntil: "domcontentloaded", timeout: boundedTimeout });
117
+ if (response && response.status() >= 400) throw new Error(`Could not open ${target.href}: HTTP ${response.status()}`);
118
+ await page.waitForTimeout(SETTLE_MS);
119
+ const observed = await page.evaluate(() => {
120
+ const visible = (element) => element.offsetParent !== null;
121
+ const controls = [...document.querySelectorAll("button, a[href], input, textarea, select, [role=button], [role=tab], [role=checkbox], [role=switch]")]
122
+ .filter((element) => element.type !== "hidden" && visible(element))
123
+ .slice(0, 80)
124
+ .map((element) => {
125
+ const tag = element.tagName.toLowerCase();
126
+ const field = ["input", "textarea", "select"].includes(tag);
127
+ const secure = element.type === "password";
128
+ const role = element.getAttribute("role") || (tag === "a" ? "link" : tag === "button" ? "button" : "");
129
+ const label = (element.labels?.[0]?.textContent || element.getAttribute("aria-label") || element.textContent || element.placeholder || element.name || element.id || "").trim().slice(0, 120);
130
+ return {
131
+ type: field ? (secure ? "SecureTextField" : "TextField") : "Button",
132
+ role,
133
+ label,
134
+ identifier: element.id || element.getAttribute("data-testid") || element.getAttribute("aria-label") || "",
135
+ isEnabled: !element.disabled && element.getAttribute("aria-disabled") !== "true",
136
+ hittable: true,
137
+ secure,
138
+ };
139
+ })
140
+ .filter((control) => control.label || control.identifier);
141
+ return {
142
+ heading: document.querySelector("h1")?.textContent?.trim() || "",
143
+ title: document.title.trim(),
144
+ controls,
145
+ };
146
+ });
147
+ const image = screenshot ? await page.screenshot({ type: "png" }) : null;
148
+ return {
149
+ url: page.url(),
150
+ screenTitle: webScreenTitle(observed, target.pathname || target.href),
151
+ elements: observed.controls,
152
+ image,
153
+ };
154
+ } finally {
155
+ await browser.close().catch(() => {});
156
+ }
157
+ }
158
+
100
159
  export function normalizeWebSeedRoutes(url, routes, limit = 5) {
101
160
  const origin = new URL(url);
102
161
  const boundedLimit = Math.max(0, Math.min(10, Number(limit) || 0));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aarwitz/tapp",
3
- "version": "0.16.0",
3
+ "version": "0.16.2",
4
4
  "mcpName": "io.github.aarwitz/tapp",
5
5
  "description": "Release contracts, autonomous QA, and evidence-backed CI gates for iOS, Android, and web.",
6
6
  "license": "MIT",
@@ -87,7 +87,7 @@
87
87
  "mobile"
88
88
  ],
89
89
  "scripts": {
90
- "test": "node --test tests/report.test.js tests/regression.test.js tests/engine.test.js tests/project-config.test.js tests/application-model.test.js tests/ui-map.test.js tests/task-runtime.test.js tests/release-contract.test.js tests/pr-selection.test.js tests/flow-runtime.test.js tests/web-explorer.test.js tests/web-session.test.js tests/web-flow.test.js tests/scenario-runtime.test.js tests/android-driver.test.js tests/android-explorer.test.js tests/android-flow.test.js tests/managed-web.test.js tests/product-operations.test.js tests/browser-product.test.js tests/browser-onboarding.test.js tests/managed-operation.test.js tests/cloud-runner.test.js tests/ci-setup.test.js tests/ci-install.test.js tests/cli.test.js tests/action.test.js tests/package-surface.test.js tests/ci-gate.test.js tests/ci-report.test.js tests/desktop-protocol.test.js tests/ios-flow-protocol.test.js",
90
+ "test": "node --test tests/report.test.js tests/regression.test.js tests/engine.test.js tests/project-config.test.js tests/application-model.test.js tests/ui-map.test.js tests/task-runtime.test.js tests/release-contract.test.js tests/pr-selection.test.js tests/flow-runtime.test.js tests/web-explorer.test.js tests/web-session.test.js tests/web-flow.test.js tests/scenario-runtime.test.js tests/android-driver.test.js tests/android-explorer.test.js tests/android-flow.test.js tests/managed-web.test.js tests/product-operations.test.js tests/browser-product.test.js tests/browser-onboarding.test.js tests/managed-operation.test.js tests/ci-setup.test.js tests/ci-install.test.js tests/cli.test.js tests/action.test.js tests/package-surface.test.js tests/ci-gate.test.js tests/ci-report.test.js tests/desktop-protocol.test.js tests/ios-flow-protocol.test.js",
91
91
  "test:browser-journey": "node --test tests/browser-journey.test.js",
92
92
  "test:browser-native": "TAPP_RUN_NATIVE_BROWSER=1 node --test tests/browser-native-journey.test.js"
93
93
  }
@@ -1,72 +0,0 @@
1
- # Browser Release Studio
2
-
3
- Status: current local-product contract as of 2026-08-08.
4
-
5
- The browser is Tapp's primary customer workflow. Web is also one application target beside iOS and
6
- Android; it is not a separate QA product. CLI, MCP, VS Code, the Action, desktop, and future managed
7
- SaaS adapt the shared product operations described in [`PRODUCT-ENGINE.md`](PRODUCT-ENGINE.md).
8
-
9
- ## Start locally
10
-
11
- ```bash
12
- npx -y @aarwitz/tapp app
13
- ```
14
-
15
- Tapp prints an authenticated one-time launch URL and opens it in the default browser. Use
16
- `--no-open` when copying the URL manually and `--port 4317` only when a fixed loopback port is
17
- needed. Drag/drop or Browse Folder copies source into a Tapp-owned workspace. Connect GitHub lists
18
- repositories authorized to the local `gh` session and makes a shallow isolated clone.
19
- `tapp app /path/to/repo` intentionally works directly in that checkout.
20
-
21
- The local server binds to `127.0.0.1`. It owns workspace paths; browser requests cannot submit an
22
- arbitrary server path. Mutations require an `HttpOnly` same-site session cookie, the exact local
23
- Origin, and an in-memory CSRF token. Application runtimes, repository credentials, and evidence stay
24
- in the local process/filesystem. This is a local trust boundary, not hosted multi-tenancy.
25
-
26
- ## Product journey
27
-
28
- 1. **Connect** a copied folder, an explicit checkout, or a repository authorized by local `gh`.
29
- 2. **Detect and choose** an iOS, Android, or web target. Continue automatically only when the target
30
- and configuration are conclusive.
31
- 3. **Build, launch, and explore** the real simulator, emulator/device, or browser surface.
32
- 4. **Understand the UI Map** through observed states, transitions, controls, provenance, and gaps.
33
- 5. **Review intent** by approving, rejecting, deferring, or constraining a compact release plan.
34
- 6. **Generate drafts** of Tasks and contracts. Drafts remain visibly untrusted.
35
- 7. **Validate** approved drafts deterministically against the real target.
36
- 8. **Promote** only validated artifacts into the canonical suite and refreshed Application Model.
37
- 9. **Gate** with autonomous evidence plus the promoted deterministic suite.
38
- 10. **Baseline** only a passing, conclusive, target-scoped gate.
39
- 11. **Install CI** by previewing and writing a reviewable repository patch. Tapp does not commit,
40
- push, create GitHub secrets, or enable branch protection.
41
-
42
- Successful semantic actions can be saved in `.tapp/flows/`; credential values are templated to
43
- environment references. Long-lived repository artifacts store binding names, not resolved secret
44
- values.
45
-
46
- ## Verified reference journey
47
-
48
- `tests/browser-journey.test.js` drives the visible local browser against a fresh CommerceDemo copy.
49
- It exercises startup, a real live web surface and semantic action, UI Map creation, Flow recording
50
- and replay, proposal review, generation, deterministic validation, promotion, a first gate,
51
- baseline-aware rerun, and CI preview.
52
-
53
- The opt-in `tests/browser-native-journey.test.js` passed on 2026-08-06 against a booted iOS
54
- simulator: the browser built and installed a disposable DemoApp checkout, ran shared target
55
- preparation and exploration, rendered an observed UI Map, drove the live surface, and saved a
56
- repository-native iOS Flow. The equivalent Android browser journey was not verified in that audit
57
- because no emulator/device was connected.
58
-
59
- This evidence proves representative local journeys. It does not prove arbitrary frameworks,
60
- production credentials, third-party services, hosted execution, or complete inference of business
61
- intent.
62
-
63
- ## Hosted relationship
64
-
65
- The future hosted application will present the same product journey through a different adapter: application
66
- accounts/organizations, GitHub App repository authorization, private storage, a durable queue, and
67
- isolated managed workers. It cannot reuse the loopback session, local `gh` authority, filesystem
68
- boundary, or in-memory ownership assumptions.
69
-
70
- The old hosted preview and `cloud/` prototype do not satisfy this boundary. Follow
71
- [`SAAS-ARCHITECTURE.md`](SAAS-ARCHITECTURE.md) and do not market or accept private repositories until
72
- [`SAAS-READINESS.md`](SAAS-READINESS.md) passes.
@@ -1,103 +0,0 @@
1
- # One Tapp product engine
2
-
3
- Status: current product-engine contract as of 2026-08-08.
4
-
5
- Tapp has several interfaces, not several products. The source of truth for customer-critical
6
- operations is [`mcp-server/src/product-operations.js`](../mcp-server/src/product-operations.js).
7
- An interface may validate its transport and render a result; it must not redefine onboarding,
8
- review, trust, baseline, or gate semantics.
9
-
10
- ## Product operation contract
11
-
12
- The shared engine owns these operations:
13
-
14
- | Operation | Authoritative result |
15
- |---|---|
16
- | `initializeProductProject` | detected targets, real exploration, Application Model, UI Map, release plan |
17
- | `readProductProject` | one current, read-only product snapshot for any interface |
18
- | `reviewProductPlan` | explicit approve/reject/defer decisions |
19
- | `generateProductPlan` | compile-checked but untrusted Task/contract drafts |
20
- | `validateProductPlan` | real-target, deterministic replay evidence |
21
- | `promoteProductPlan` | canonical Tasks/contracts, refreshed model/plan, updated map coverage |
22
- | `prepareProductCi` / `installProductCi` | target-aware workflow and machine-readable CI manifest |
23
- | `runProductGate` | autonomous evidence plus the committed deterministic suite and one gate decision |
24
- | `createProductBaseline` | conclusive, platform-and-target-specific comparison state |
25
-
26
- Deterministic contract execution is in
27
- [`mcp-server/src/product-execution.js`](../mcp-server/src/product-execution.js). It invokes platform
28
- executors directly; MCP does not shell through the CLI, and the browser does not shell through MCP.
29
-
30
- ## Interfaces
31
-
32
- ```text
33
- Browser Release Studio ─┐
34
- CLI ├── product-operations ── application model / UI Map / Tasks / contracts
35
- MCP ┘ │
36
- └── deterministic executors / portable gate / evidence
37
-
38
- VS Code ── MCP client
39
- Desktop ── canonical artifact reader (migration to operation client remains)
40
- Action ── portable gate adapter
41
- Hosted ── tenant-aware SaaS adapter + queued isolated shared-operation workers (not built)
42
- ```
43
-
44
- Current convergence:
45
-
46
- - the browser calls only shared product operations;
47
- - CLI initialization, plan lifecycle, deterministic draft validation, promotion, gate/baseline
48
- lifecycle, and CI installation call the same operations. Native build preparation remains at the
49
- adapter boundary and passes a resolved `.app` or APK into the shared gate;
50
- - MCP initialization, plan lifecycle, deterministic draft validation, promotion, baseline, and CI
51
- installation call the same operations;
52
- - the GitHub Action and `runProductGate` call the same portable gate and evidence protocol;
53
- - VS Code remains a thin MCP client;
54
- - desktop reads the same `.tapp` artifacts but still has legacy import/build orchestration. It is
55
- retained, not the launch UX, until that orchestration is removed;
56
- - `cloud/runner` is retained prototype evidence for exact checkout, versioned operation envelopes,
57
- leases, and cleanup. It is not the production hosted adapter or an adequate arbitrary-customer
58
- isolation boundary. The new SaaS must call these shared operations only through the tenant-aware,
59
- queued worker contract in [`SAAS-ARCHITECTURE.md`](SAAS-ARCHITECTURE.md).
60
-
61
- ## Canonical repository protocol
62
-
63
- New product behavior writes only `.tapp/`:
64
-
65
- ```text
66
- .tapp/
67
- project.json # actors, env binding names, controlled lifecycle; never secret values
68
- application-model.json # detected/observed/declared product facts
69
- ui-map.json # grounded screen/action/transition graph
70
- release-plan.json # proposals and explicit human decisions
71
- tasks/ # reusable deterministic semantic operations
72
- contracts/ # reviewed business guarantees
73
- baselines/<platform>/ # conclusive target-specific comparison state
74
- ci.json # generated CI installation manifest
75
- ```
76
-
77
- `.tapp.yml` is the canonical run configuration. Existing `.autotap.yml`, `.autotap/`, and
78
- `AUTOTAP_*` inputs remain readable during migration, but new examples and output use the Tapp names.
79
- Do not add another configuration format. Migration readers may normalize old input into the canonical
80
- model; only an explicit reviewed operation may write new repository artifacts.
81
-
82
- ## Anti-duplication rules
83
-
84
- 1. Trust states (`pending`, `approved`, `validated-draft`, `promoted`) are computed by the engine.
85
- 2. Interfaces render `readProductProject`; they do not infer readiness from file existence.
86
- 3. Re-exploration refreshes evidence while preserving reviewed decisions everywhere.
87
- 4. Promotion refreshes the Application Model immediately; no interface may show stale pre-promotion
88
- requirements.
89
- 5. Baselines are identified by platform and stable target id everywhere.
90
- 6. An adapter-specific feature is not complete until its engine operation is useful without that
91
- adapter.
92
- 7. Equivalence tests should assert artifacts and structured results, not merely matching copy.
93
-
94
- ## Remaining migration
95
-
96
- The next safe convergence work is deliberately narrow:
97
-
98
- 1. replace desktop import/build orchestration with a local product-operation client;
99
- 2. delete the two desktop detection/scaffolding paths only after equivalence fixtures pass;
100
- 3. implement managed account, organization, and tenant authorization before connecting repositories;
101
- 4. implement scoped GitHub authorization, private evidence, and disposable per-job
102
- identity/simulator/credential isolation before accepting customer code;
103
- 5. preserve CLI/MCP/VS Code/Action as adapters—do not rebuild their product logic.
@@ -1,271 +0,0 @@
1
- # Application model and `tapp init`
2
-
3
- `tapp init` is the deterministic import, exploration, and planning entrypoint of Tapp's customer
4
- journey. It turns a repository into three platform-neutral, repository-native artifacts:
5
-
6
- - `.tapp/ui-map.json` — observed UI states, controls, and transitions from real exploration;
7
- - `.tapp/application-model.json` — what Tapp can support with evidence;
8
- - `.tapp/release-plan.json` — the compact set of committed and proposed business guarantees a
9
- customer must review before generation.
10
-
11
- Plain `tapp init` performs source/artifact inspection only. `tapp init --explore` additionally uses
12
- the same keyless QA engine as `tapp qa` to build/install/launch or connect to one selected real
13
- target, merge the observed map, and construct the model and plan from that runtime evidence. It
14
- does not generate or approve tests, call AI, or claim contract validation. When iOS repository
15
- resolution actually builds and installs the detected Xcode container, the model records the exact
16
- scheme as runtime-observed validation and removes the corresponding confirmation blocker. Merely
17
- supplying a bundle id or prebuilt `.app` does not prove repository build configuration.
18
-
19
- ## First inspection
20
-
21
- ```bash
22
- # Read-only preview. For web, provide the owned runtime URL if already known.
23
- tapp init . --url http://127.0.0.1:3000 --dry-run \
24
- --json-out /tmp/tapp-init-preview.json
25
-
26
- # Create canonical artifacts. Existing files are never overwritten implicitly.
27
- tapp init . --url http://127.0.0.1:3000
28
-
29
- # Build/start the detected web target, explore it, persist its UI Map, then stop it.
30
- tapp init . --explore --platform web --actions 40 --timeout 600
31
-
32
- # Or connect to an already-running owned environment.
33
- tapp init . --explore --platform web --url http://127.0.0.1:3000 \
34
- --actions 40 --timeout 600
35
-
36
- # iOS can resolve a repository/Xcode container/.app/bundle id and build when needed.
37
- tapp init . --explore --platform ios --target .
38
-
39
- # Android can install an APK, then launch the explicit application id.
40
- tapp init . --explore --platform android \
41
- --apk app/build/outputs/apk/debug/app-debug.apk --app-id com.acme.app
42
-
43
- # Re-inspect after source/UI Map changes while preserving explicit review decisions.
44
- tapp init . --url http://127.0.0.1:3000 --refresh
45
-
46
- # Re-explore after review without losing approve/reject/defer choices.
47
- tapp init . --refresh --explore --platform web --url http://127.0.0.1:3000
48
- ```
49
-
50
- MCP clients use `tapp_init` with `operation: inspect|write|refresh|explore`. `inspect` is the safe
51
- default. `explore` writes real evidence, so the CLI rejects `--explore --dry-run`; the CLI also
52
- requires `--refresh --explore` once model/plan artifacts exist. Credentials are passed only to the
53
- runtime and are never written into the model, map, or plan.
54
-
55
- Successful repository-driven iOS build validation is portable and durable. The application model
56
- stores the repository-relative container, scheme, configuration, bundle id, and a
57
- `tapp-capture:<id>` evidence reference—never the local DerivedData or checkout path. A later
58
- source-only `tapp init --refresh` retains that validation when it still names the same detected
59
- container. Tapp does not infer equivalent proof from an installed application, an explicit bundle
60
- id, or a prebuilt artifact; those paths can demonstrate runtime reachability but cannot silently
61
- confirm the repository's Xcode scheme.
62
-
63
- ## Actors and credential bindings
64
-
65
- Configure named actors once instead of repeating credentials or session policy across tests:
66
-
67
- ```bash
68
- tapp actor set alice . --role member --session isolated --provisioning seeded \
69
- --credential email=ALICE_EMAIL --credential password=ALICE_PASSWORD
70
- tapp actor set bob . --role member --session isolated --provisioning seeded \
71
- --credential email=BOB_EMAIL --credential password=BOB_PASSWORD
72
- tapp actor list .
73
- tapp init . --refresh
74
- ```
75
-
76
- This writes `.tapp/project.json`. The file contains roles, `default`/`isolated` session policy,
77
- provisioning mode, same-origin lifecycle declarations, and environment-variable *names*. The CLI
78
- and MCP `tapp_actor_config` reject credential values and refuse to replace an actor without an
79
- explicit `--replace`/`replace: true`. Contracts refer to `$ALICE_EMAIL`-style placeholders. Tapp
80
- merges those reviewed placeholders with the central configuration, blocks missing/conflicting
81
- bindings, and never copies resolved values into the application model, release plan, UI Map, CI
82
- manifest, or generated workflow.
83
-
84
- When web `--url` is omitted, Tapp selects one detected browser target, runs only its internally
85
- derived lockfile-backed install command, runs its declared build script when present, and starts its
86
- `start`, `dev`, `serve`, or `preview` package script with argument-array process execution (never
87
- generated shell source). A static site with no script uses Tapp's local read-only static server. The
88
- runtime binds to an available loopback port, writes its log under the Tapp runtime directory, and is
89
- terminated after exploration even when QA fails. Multiple web targets, an unlocked dependency
90
- graph, an unrecognized start path, or backend-specific configuration produce explicit remediation;
91
- provide `--target` and/or an already-running owned `--url` in those cases. Running repository build
92
- scripts executes repository code and should only be used for a checkout the customer trusts.
93
- The managed loop never persists its ephemeral loopback URL as customer configuration. The model
94
- records `runtime.management: tapp-managed`, and the portable gate/Action reconstructs the same
95
- start/wait/stop lifecycle later. An explicitly supplied owned URL remains `customer-managed`.
96
-
97
- ## What the model records
98
-
99
- Application Model v1 records:
100
-
101
- - detected iOS simulator, Android application, and browser targets;
102
- - inspectable build commands, project/module/container paths, scheme candidates, application ids,
103
- owned URLs, missing confirmations, and exact runtime-observed target validation where Tapp itself
104
- completed the repository build/install path;
105
- - actors, roles, provisioning modes, credential requirements/environment bindings, and
106
- session-isolation boundaries without credential values;
107
- - business entities and capabilities explicitly declared by reviewed contracts or conservatively
108
- derived from reusable Task names;
109
- - authored critical journeys, revenue paths, and cross-actor system invariants;
110
- - the shared UI Map's observed state/transition/control counts and uncovered ids;
111
- - the latest import exploration's verdict and explicit inconclusive status, when available;
112
- - existing Tasks and contracts;
113
- - blocking requirements and exact remediation.
114
-
115
- Every fact identifies its evidence class. The current deterministic importer uses:
116
-
117
- - `source-observed` for repository files and build metadata;
118
- - `runtime-observed` for a successful exact target build/install/exploration, with portable evidence;
119
- - `reviewed-artifact` for committed Tasks, contracts, and UI Map evidence;
120
- - `task-derived` or another source-derived status when a fact still requires review;
121
- - `authored-unvalidated` when a committed contract exists without current-revision replay proof.
122
-
123
- Runtime observation, source inference, optional AI proposals, and human decisions must not be
124
- collapsed into one confidence label. The artifact explicitly records that remote AI was not used.
125
-
126
- ## Release-plan quality
127
-
128
- The deterministic planner starts with committed contracts, then proposes only evidence-grounded
129
- gaps:
130
-
131
- - a conservative cross-actor propagation guarantee when two explicitly configured isolated actors,
132
- deterministic setup/teardown, compatible authentication/precondition screens, and an exact
133
- content-producing Task output jointly prove that the proposal is grounded;
134
- - reusable Tasks not composed by a reviewed contract;
135
- - uncovered UI states carrying business signals such as authentication, pricing, checkout,
136
- account, messaging, or settings behavior.
137
-
138
- Error pages, blank pages, loading surfaces, changelogs, and generic feature-description pages remain
139
- visible as UI Map coverage gaps but do not automatically become business contracts. The target is
140
- approximately 5–15 contracts for a sufficiently rich product, not an artificial quota for a small
141
- fixture. Every proposal includes business value, risk, criticality, actors, platforms, grounding,
142
- and the real-surface validation required before it can be trusted.
143
-
144
- ## Explicit review
145
-
146
- ```bash
147
- tapp plan show .tapp/release-plan.json
148
- tapp plan review .tapp/release-plan.json \
149
- --approve signInWorks,checkoutWorks \
150
- --reject marketingPageReachable \
151
- --defer adminAuditWorks
152
-
153
- # Only after review: generate grounded Task + contract drafts under .tapp/proposals/.
154
- tapp plan generate .tapp/release-plan.json --project-dir .
155
-
156
- # Replay the draft on the real target and attach evidence to the plan.
157
- # Omit --url to build/start/stop the detected managed browser target.
158
- tapp plan validate .tapp/release-plan.json --project-dir . --platform web
159
- # Or connect to an already-running owned environment.
160
- tapp plan validate .tapp/release-plan.json --project-dir . \
161
- --platform web --url http://127.0.0.1:3000
162
-
163
- # Explicitly accept only fully validated drafts into canonical reviewed locations.
164
- tapp plan promote .tapp/release-plan.json --project-dir . \
165
- --item checkoutWorks
166
- ```
167
-
168
- The MCP equivalent is `tapp_release_plan` with `read|review|generate|validate|promote`. Review
169
- changes decision metadata only. It cannot silently
170
- edit a Task, contract, selector, or assertion. On `tapp init --refresh`, decisions, constraints, and
171
- review notes are carried forward by stable item id; reviewed items no longer derived from current
172
- evidence are retained and marked stale instead of disappearing.
173
-
174
- A source-only refresh preserves recorded replay evidence. `tapp init --refresh --explore` carries
175
- the history forward but invalidates trust for affected generated Tasks and contracts: prior
176
- platform results move to historical evidence, status becomes `requires-revalidation`, and replay is
177
- required before the draft can be trusted against the newly observed revision. Exploration never
178
- silently self-heals or accepts the prior selector path.
179
-
180
- The macOS desktop Coverage experience reads these same files. Its **Application** tab explains
181
- detected targets, actors, capabilities, journeys, Tasks, contracts, and exact remediation. Its
182
- **Release Plan** tab writes explicit approve/reject/defer decisions atomically into the canonical
183
- plan while preserving fields from newer engine versions; committed contract intent is not editable
184
- through these proposal controls. Flow Map merges the repository `.tapp/ui-map.json` with current
185
- run evidence instead of building a separate desktop-only graph.
186
-
187
- Schema compatibility is exercised by the repository's protocol tests and retained desktop reader.
188
-
189
- `plan generate` handles only explicitly approved proposals. Grounded cross-actor proposals preserve
190
- actor-attributed Task calls, captured output variables, bounded eventual assertions, and the
191
- reviewed project lifecycle, then compile through the isolated Scenario executor. Existing
192
- Task-backed proposals compose those reviewed Tasks. For UI-Map-only proposals, it finds an observed path from each platform's
193
- recorded entry state, deduplicates shared semantic transitions into compositional Task drafts under
194
- `.tapp/proposals/tasks/`, grounds every Task in exact node/edge ids, and writes the contract draft
195
- under `.tapp/proposals/contracts/`. Proposal Tasks are visible only to proposal contracts; an
196
- ordinary committed contract or CI glob cannot silently consume one.
197
-
198
- Generation blocks when entry-state evidence is missing, the target is unreachable, an observed
199
- action cannot be represented deterministically, or platform paths require incompatible semantic
200
- composition. It never overwrites a draft, statically compiles each declared platform, and marks all
201
- outputs untrusted. Missing non-secret Task inputs stay blocked until the plan has explicit bindings;
202
- standard email/password secrets remain placeholders. Successful grounding and compilation are not
203
- real-surface evidence and never promote drafts into `.tapp/tasks` or `.tapp/contracts`.
204
-
205
- `plan validate` invokes the ordinary deterministic contract executor and records pass/fail evidence
206
- per declared platform. A multi-platform draft remains only partially validated until every declared
207
- platform passes. Failed replay remains visible and sets `trusted: false`; there is no selector
208
- substitution or automatic assertion update.
209
-
210
- `plan promote` is the explicit acceptance boundary. It refuses any contract or generated Task that
211
- has not passed every declared platform, preflights every destination, never overwrites a reviewed
212
- artifact, moves accepted files from `.tapp/proposals/{tasks,contracts}` into
213
- `.tapp/{tasks,contracts}`, and applies their exact node/edge coverage to the canonical UI Map.
214
- Shared Task paths in still-unpromoted proposals are rewritten to the canonical file. Promotion does
215
- not commit, push, or install CI; the resulting repository patch remains reviewable by the customer.
216
-
217
- ## Baseline and CI handoff
218
-
219
- After promotion, complete the local onboarding loop with:
220
-
221
- ```bash
222
- # Runs the ordinary autonomous QA + committed suites gate. Builds native targets when possible;
223
- # web targets can be detected, built, started, awaited, and stopped without a durable URL.
224
- tapp baseline create . --platform web
225
-
226
- # Or import an already-retained successful full-gate report after review.
227
- tapp baseline create . --platform web --from /path/to/tapp-report.json
228
-
229
- # Generate one target-aware job per model target plus a machine-readable manifest.
230
- tapp ci install . --action-ref aarwitz/tapp@v0.13.1
231
- ```
232
-
233
- Baseline creation rejects non-gate JSON, missing or mismatched target identity, platform mismatch,
234
- failed Flows/Scenarios/contracts, `blocked`, and `inconclusive`. It writes atomically to
235
- `.tapp/baselines/<platform>/<target-id>.json` and requires `--replace` to supersede reviewed
236
- evidence. Capture-local paths are replaced with portable `tapp-capture:` references before the
237
- repository artifact is written. The gate also checks baseline platform and target identity before
238
- comparing findings. On iOS, the same validated launch arguments and string-valued launch
239
- environment are passed to autonomous exploration and deterministic Flow/contract replay; invalid
240
- JSON or unsupported value types fail before execution rather than silently testing different app
241
- configurations.
242
-
243
- CI installation writes `.github/workflows/tapp.yml` and `.tapp/ci.json`, never overwrites by
244
- default, and refuses unresolved iOS schemes, Android ids, browser lockfiles, or runtimes. The
245
- workflow uses exact contract paths, maps each actor environment binding to a same-named GitHub
246
- Secret, uses the first/default actor for autonomous-login inputs, preserves the remaining bindings
247
- for deterministic multi-actor replay, and supports managed web startup, Android emulator
248
- provisioning, and the target-specific baseline. It does not commit,
249
- push, enable branch protection, or create remote resources. Use MCP `tapp_ci_setup` for the same
250
- read-only render, baseline import, and guarded install engine.
251
-
252
- ## Current boundary
253
-
254
- Repository detection, one-target real exploration, first-map merge, evidence classification,
255
- runtime-observed iOS scheme confirmation, durable source-only refresh, deterministic planning, safe
256
- persistence, approve/reject/defer review, and compile-checked Task-backed draft generation are
257
- implemented. An empty map or a latest exploration marked inconclusive remains a blocking
258
- requirement; observing a login wall is not treated as useful coverage.
259
-
260
- `tapp init` does not yet orchestrate every detected target in one invocation, provision arbitrary
261
- web backends/services, automatically replay every approved draft, or promote validated drafts without
262
- explicit customer acceptance. Baseline creation and a reviewable per-target GitHub CI patch are now
263
- implemented as explicit post-promotion commands, but the generated workflow has not yet passed on
264
- current GitHub-hosted iOS, Android, and web runners. Task generation currently handles observed
265
- reachable navigation. Deterministic business planning is deliberately limited to cross-actor
266
- content propagation and one checkout-to-order-history persistence pattern supported by exact Task
267
- input/output, screen, actor, UI Map, and lifecycle evidence. General forms, broader payment shapes,
268
- dynamic value capture, messaging/reactions, role-asymmetric invariants, and incompatible platform
269
- journeys still require reviewed authoring. Optional
270
- AI business reasoning is also not wired into this path. Those missing stages remain completion
271
- blockers.
package/docs/scenarios.md DELETED
@@ -1,95 +0,0 @@
1
- # Multi-actor Scenarios
2
-
3
- A Scenario is Tapp's low-level deterministic multi-actor execution format in `.tapp/scenarios/*.yml`. It uses the same semantic actions, polling assertions, timeouts, evidence markers, and merge policy as a Flow, but adds isolated named actors, shared variables, and explicit lifecycle steps. The customer-facing business authoring layer is a TypeScript [release contract](release-contracts.md), which composes reusable Tasks and compiles to this runtime instead of duplicating UI steps.
4
-
5
- Ordinary replay is keyless. AI may propose a Scenario during authoring, but no model, API key, or coding agent participates when CI executes it.
6
-
7
- ## Current support
8
-
9
- Web replay is implemented through one isolated Playwright browser context per actor. Cookies, local storage, and in-browser session state cannot leak between actors; all contexts point at the same deployed application and backend. The Action, portable gate, CLI, and MCP surface all consume the same file.
10
-
11
- iOS and Android still support sequential account switching inside ordinary Flows, but do not yet have first-class isolated multi-actor Scenario drivers. Tapp rejects those platform combinations instead of presenting sequential login/logout as equivalent proof.
12
-
13
- ## Contract
14
-
15
- ```yaml
16
- name: Alice publishes and Bob sees it
17
- kind: scenario
18
- platform: web
19
- url: http://127.0.0.1:4180
20
- timeoutMs: 6000
21
- vars: # shared deterministic data
22
- POST: Scenario post 7319
23
- actors:
24
- alice:
25
- vars: # actor-scoped credentials/session inputs
26
- EMAIL: alice@example.test
27
- PASSWORD: demo
28
- bob:
29
- vars:
30
- EMAIL: bob@example.test
31
- PASSWORD: demo
32
- setup:
33
- - request: # bounded, same-origin HTTP; never arbitrary shell
34
- method: POST
35
- path: /__tapp/reset
36
- status: 200
37
- steps:
38
- - actor: alice
39
- type: { field: Email, value: $EMAIL }
40
- - actor: alice
41
- type: { field: Password, value: $PASSWORD }
42
- - actor: alice
43
- tap: Sign in
44
- - actor: alice
45
- type: { field: Post text, value: $POST }
46
- - actor: alice
47
- tap: Publish
48
- - actor: bob
49
- assert_exists: { target: $POST, timeoutMs: 6000 }
50
- teardown:
51
- - request: { method: POST, path: /__tapp/reset, status: 200 }
52
- ```
53
-
54
- - `actors` must contain at least two names. Every journey step names one of them.
55
- - Actor variables override shared variables. A committed value such as `$ALICE_PASSWORD` resolves only that explicitly referenced environment variable at run time; Tapp does not serialize the surrounding environment.
56
- - `setup` and `teardown` currently accept bounded HTTP request steps on the target origin. Teardown runs after a journey failure so state is still cleaned up.
57
- - Flow assertions poll until their bounded timeout. This models eventual consistency without blind sleeps or unbounded retries. A condition that never becomes true fails visibly.
58
- - Typed values are not written to step evidence. Results include actor, action, selector, status, and error; failures capture that actor's screen and final screenshots for all actors.
59
- - A failed Scenario always blocks the release gate, independently of whether autonomous single-user exploration found a problem.
60
-
61
- ## Run it
62
-
63
- ```bash
64
- tapp scenario validate .tapp/scenarios/social-system.yml
65
- ALICE_EMAIL=alice@example.test ALICE_PASSWORD=demo \
66
- BOB_EMAIL=bob@example.test BOB_PASSWORD=demo \
67
- tapp scenario run .tapp/scenarios/social-system.yml
68
-
69
- tapp ci --platform web --url http://127.0.0.1:4180 \
70
- --scenarios '.tapp/scenarios/*.yml' \
71
- --json-out tapp-report.json --md-out tapp-report.md
72
- ```
73
-
74
- GitHub Action:
75
-
76
- ```yaml
77
- - uses: aarwitz/tapp@main
78
- with:
79
- platform: web
80
- url: http://127.0.0.1:4180
81
- scenarios: .tapp/scenarios/*.yml
82
- ```
83
-
84
- MCP clients call `tapp_scenario_run` with `scenarioPath`, or an inline reviewed Scenario. The structured result identifies `kind: scenario`, actual executed/total steps, and actor-tagged steps.
85
-
86
- ## Verified fixture and boundaries
87
-
88
- `SocialDemo/.tapp/contracts/social-system.contract.ts` is the reference system guarantee; the Scenario remains its low-level execution proof and backwards-compatible escape hatch. On 2026-08-04 the contract passed 41/41 compiled steps using two isolated contexts against one delayed shared backend. With `SOCIAL_DEMO_FAULT=hide-cross-actor-posts`, the unchanged contract failed for Bob at 19/41 and the portable merge gate exited non-zero specifically because one release contract failed.
89
-
90
- The fixture's `.tapp/project.json` is the central actor contract. It records Alice and Bob's
91
- roles, isolated sessions, seeded provisioning, reset lifecycle, and four environment-variable
92
- names. The release contract and Scenario consume those names; neither stores the public fixture
93
- values. Customer values belong in the local environment or CI secret store.
94
-
95
- This proves the contract and web implementation, not universal multi-user reliability. Real customers still need reset/provisioning hooks or dedicated test data, enough accessibility semantics to select controls, and a test backend whose eventual-consistency budget is known.