@aarwitz/tapp 0.17.0-rc.6 → 0.17.0-rc.7

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/bin/tapp.js CHANGED
@@ -1460,9 +1460,15 @@ switch (command) {
1460
1460
  const runs = roots
1461
1461
  .flatMap((root) => fs.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path.join(root, e.name)))
1462
1462
  .sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
1463
- const wanted = rest[0] && rest[0] !== "latest" ? runs.find((r) => path.basename(r) === rest[0]) : runs[0];
1463
+ const hasMarkers = (dir) => fs.existsSync(path.join(dir, "ocqa-markers.txt"));
1464
+ // `latest` (default) resolves to the newest *exploration* capture — the captures directory is
1465
+ // also full of flow-*/scenario-* evidence dirs with no ocqa-markers.txt, and picking the newest
1466
+ // of those made `tapp report` fail even though valid exploration captures existed. An explicitly
1467
+ // named capture is honored as-is so a non-exploration capture still gets a clear "no markers".
1468
+ const explicit = rest[0] && rest[0] !== "latest";
1469
+ const wanted = explicit ? runs.find((r) => path.basename(r) === rest[0]) : runs.find(hasMarkers);
1464
1470
  if (!wanted) {
1465
- bad("No captures found", rest[0] ? `no capture named "${rest[0]}"` : "run a QA exploration first");
1471
+ bad("No captures found", explicit ? `no capture named "${rest[0]}"` : "run an exploration first (no capture with exploration markers was found)");
1466
1472
  process.exit(1);
1467
1473
  }
1468
1474
  const { writeHtmlReport } = await import(path.join(packageRoot, "mcp-server", "src", "html-report.js"));
@@ -1472,7 +1478,9 @@ switch (command) {
1472
1478
  process.exit(1);
1473
1479
  }
1474
1480
  ok("Evidence report", out);
1475
- spawnSync("open", [out], { stdio: "ignore" });
1481
+ // Only launch a browser from an interactive terminal — agents, scripts, and tests that invoke
1482
+ // `tapp report` non-interactively get the path without a surprise GUI window.
1483
+ if (process.stdout.isTTY) spawnSync("open", [out], { stdio: "ignore" });
1476
1484
  break;
1477
1485
  }
1478
1486
 
@@ -14,6 +14,22 @@ const SEV_COLOR = { critical: "#cf222e", high: "#bc4c00", medium: "#9a6700", low
14
14
 
15
15
  const esc = (s) => String(s ?? "").replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[c]));
16
16
 
17
+ const VALID_PLATFORMS = new Set(["web", "android", "ios"]);
18
+
19
+ // Recover a capture's platform when no in-memory report is available. Prefer the run's persisted
20
+ // metadata (ui-map.json → app.platforms) — it is authoritative and survives a renamed folder — and
21
+ // fall back to the capture-id prefix (web-*/android-*, ios unprefixed) only for legacy captures that
22
+ // predate the map. Defaults to ios if nothing is resolvable, matching buildQaReport's own default.
23
+ export function capturePlatform(captureDir) {
24
+ try {
25
+ const map = JSON.parse(fs.readFileSync(path.join(captureDir, "ui-map.json"), "utf8"));
26
+ const fromMap = (map?.app?.platforms || []).find((p) => VALID_PLATFORMS.has(p));
27
+ if (fromMap) return fromMap;
28
+ } catch { /* no map, unreadable, or no valid platform — fall back to the id prefix */ }
29
+ const base = path.basename(captureDir);
30
+ return base.startsWith("web-") ? "web" : base.startsWith("android-") ? "android" : "ios";
31
+ }
32
+
17
33
  // Two capture layouts exist: web runs write state_*.png at the capture root; iOS runs
18
34
  // export XCUITest attachments into screenshots/ as UUID files with a manifest carrying
19
35
  // the human-readable state_N_<Screen> names.
@@ -50,7 +66,11 @@ function collectShots(captureDir) {
50
66
  }
51
67
 
52
68
  export function writeHtmlReport(captureDir, { report, label = "", recordingWarning = "" } = {}) {
53
- const r = report || buildQaReport(path.join(captureDir, "ocqa-markers.txt"));
69
+ // Rebuild path (e.g. `tapp report`): no report object is passed, so buildQaReport would default to
70
+ // native — rendering a web/android page with the wrong "Checked / Not checked" scope, overclaiming
71
+ // native checks it never ran. Recover the run's real platform (metadata first, id prefix as a
72
+ // legacy fallback). When a report object IS passed (explore/init), it is already correct.
73
+ const r = report || buildQaReport(path.join(captureDir, "ocqa-markers.txt"), { platform: capturePlatform(captureDir) });
54
74
  if (!r) return null;
55
75
 
56
76
  const shots = collectShots(captureDir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aarwitz/tapp",
3
- "version": "0.17.0-rc.6",
3
+ "version": "0.17.0-rc.7",
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",