@swmansion/argent 0.20.1-next.7 → 0.20.1-next.8
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/dist/tool-server.cjs +120 -47
- package/package.json +1 -1
- package/skills/argent-create-flow/references/flow-yaml.md +3 -1
- package/skills/argent-create-flow/references/live-authoring.md +2 -0
- package/skills/argent-create-flow/references/reliability-and-recovery.md +3 -0
- package/skills/argent-qa-flows/SKILL.md +1 -1
package/dist/tool-server.cjs
CHANGED
|
@@ -16776,10 +16776,10 @@ var require_browser = __commonJS({
|
|
|
16776
16776
|
exports2.useColors = useColors;
|
|
16777
16777
|
exports2.storage = localstorage();
|
|
16778
16778
|
exports2.destroy = /* @__PURE__ */ (() => {
|
|
16779
|
-
let
|
|
16779
|
+
let warned2 = false;
|
|
16780
16780
|
return () => {
|
|
16781
|
-
if (!
|
|
16782
|
-
|
|
16781
|
+
if (!warned2) {
|
|
16782
|
+
warned2 = true;
|
|
16783
16783
|
console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");
|
|
16784
16784
|
}
|
|
16785
16785
|
};
|
|
@@ -109158,12 +109158,16 @@ function settleWithin(p, ms, signal) {
|
|
|
109158
109158
|
};
|
|
109159
109159
|
p.then(
|
|
109160
109160
|
(value) => finish({ type: "value", value }),
|
|
109161
|
-
(err) =>
|
|
109161
|
+
(err) => {
|
|
109162
|
+
const cause = err instanceof Error ? err : new Error(String(err));
|
|
109163
|
+
finish({ type: "error", error: cause.message, cause });
|
|
109164
|
+
}
|
|
109162
109165
|
);
|
|
109163
109166
|
if (signal?.aborted) return finish({ type: "aborted" });
|
|
109164
109167
|
const onAbort = () => finish({ type: "aborted" });
|
|
109165
109168
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
109166
109169
|
teardown.push(() => signal?.removeEventListener("abort", onAbort));
|
|
109170
|
+
if (ms === void 0) return;
|
|
109167
109171
|
const timer = setTimeout(() => finish({ type: "timeout" }), Math.max(0, ms));
|
|
109168
109172
|
teardown.push(() => clearTimeout(timer));
|
|
109169
109173
|
});
|
|
@@ -145165,19 +145169,18 @@ async function queryVegaTree(device) {
|
|
|
145165
145169
|
|
|
145166
145170
|
// ../tool-server/src/tools/flows/flow-tree.ts
|
|
145167
145171
|
async function fetchFlowTree(registry2, device, launchedNativeApp) {
|
|
145168
|
-
|
|
145169
|
-
|
|
145170
|
-
|
|
145171
|
-
|
|
145172
|
-
|
|
145173
|
-
|
|
145174
|
-
|
|
145175
|
-
|
|
145176
|
-
|
|
145177
|
-
|
|
145178
|
-
|
|
145179
|
-
|
|
145180
|
-
return fetchTree(registry2, device);
|
|
145172
|
+
const source = FLOW_TREE_SOURCES[device.platform];
|
|
145173
|
+
if (!source) return fetchTree(registry2, device);
|
|
145174
|
+
return source(registry2, device, launchedNativeApp);
|
|
145175
|
+
}
|
|
145176
|
+
var FLOW_TREE_SOURCES = {
|
|
145177
|
+
ios: (registry2, device, launchedNativeApp) => queryFullHierarchyTree(registry2, device, launchedNativeApp),
|
|
145178
|
+
android: (registry2, device) => queryAndroidFullHierarchy(registry2, device),
|
|
145179
|
+
chromium: (registry2, device) => queryChromiumTree(registry2, device),
|
|
145180
|
+
vega: (_registry, device) => queryVegaTree(device)
|
|
145181
|
+
};
|
|
145182
|
+
function supportsFlowTree(platform) {
|
|
145183
|
+
return FLOW_TREE_SOURCES[platform] !== void 0;
|
|
145181
145184
|
}
|
|
145182
145185
|
|
|
145183
145186
|
// ../tool-server/src/tools/flows/flow-add-step.ts
|
|
@@ -145511,11 +145514,16 @@ function runSequenceOutcome(result) {
|
|
|
145511
145514
|
}
|
|
145512
145515
|
return void 0;
|
|
145513
145516
|
}
|
|
145517
|
+
var NESTED_ORCHESTRATORS = /* @__PURE__ */ new Map([
|
|
145518
|
+
[FLOW_EXECUTE_TOOL_ID, flowExecuteOutcome],
|
|
145519
|
+
[RUN_SEQUENCE_TOOL_ID, runSequenceOutcome]
|
|
145520
|
+
]);
|
|
145521
|
+
function isNestedOrchestratorTool(tool) {
|
|
145522
|
+
return NESTED_ORCHESTRATORS.has(tool);
|
|
145523
|
+
}
|
|
145514
145524
|
function nestedOrchestratorOutcome(tool, result) {
|
|
145515
145525
|
if (!isRecord(result)) return void 0;
|
|
145516
|
-
|
|
145517
|
-
if (tool === RUN_SEQUENCE_TOOL_ID) return runSequenceOutcome(result);
|
|
145518
|
-
return void 0;
|
|
145526
|
+
return NESTED_ORCHESTRATORS.get(tool)?.(result);
|
|
145519
145527
|
}
|
|
145520
145528
|
|
|
145521
145529
|
// ../tool-server/src/tools/flows/flow-pixels.ts
|
|
@@ -145659,6 +145667,7 @@ var FOCUS_REPORTING_SOURCES = /* @__PURE__ */ new Set([
|
|
|
145659
145667
|
]);
|
|
145660
145668
|
var SETTLE_POLL_MS = 250;
|
|
145661
145669
|
var SETTLE_TIMEOUT_MS = 3e3;
|
|
145670
|
+
var SETTLE_MIN_READS = 2;
|
|
145662
145671
|
var MAX_SCROLL_ITERATIONS = 25;
|
|
145663
145672
|
var SCROLL_INCREMENT = 0.5;
|
|
145664
145673
|
var MIN_SCROLL_INCREMENT = 0.05;
|
|
@@ -145709,19 +145718,34 @@ function flowSelectorToFrame(tree, sel) {
|
|
|
145709
145718
|
}
|
|
145710
145719
|
return void 0;
|
|
145711
145720
|
}
|
|
145712
|
-
|
|
145721
|
+
function provenTreeOutage(env) {
|
|
145722
|
+
const proven = env.treeOutage?.proven;
|
|
145723
|
+
return proven && proven.deviceId === env.device.id ? proven.error : void 0;
|
|
145724
|
+
}
|
|
145725
|
+
function readFlowTree(env) {
|
|
145726
|
+
return fetchFlowTree(env.registry, env.device, env.launchedNativeApp).then((data) => {
|
|
145727
|
+
if (env.treeOutage) env.treeOutage.proven = void 0;
|
|
145728
|
+
return data;
|
|
145729
|
+
});
|
|
145730
|
+
}
|
|
145731
|
+
async function settleTree(env, opts = {}) {
|
|
145713
145732
|
const deadline = Date.now() + SETTLE_TIMEOUT_MS;
|
|
145714
145733
|
let prevFp;
|
|
145715
145734
|
let prevTree;
|
|
145716
145735
|
let lastError;
|
|
145736
|
+
let reads = 0;
|
|
145717
145737
|
for (; ; ) {
|
|
145718
145738
|
if (env.signal?.aborted) return void 0;
|
|
145739
|
+
const proven = provenTreeOutage(env);
|
|
145740
|
+
if (opts.skipProvenOutage && proven) throw proven;
|
|
145741
|
+
const read = await settleWithin(readFlowTree(env), void 0, env.signal);
|
|
145719
145742
|
let tree;
|
|
145720
|
-
|
|
145721
|
-
|
|
145722
|
-
}
|
|
145723
|
-
lastError =
|
|
145743
|
+
if (read.type === "value") {
|
|
145744
|
+
tree = read.value.tree;
|
|
145745
|
+
} else if (read.type === "error") {
|
|
145746
|
+
lastError = read.cause;
|
|
145724
145747
|
}
|
|
145748
|
+
reads += 1;
|
|
145725
145749
|
if (env.signal?.aborted) return void 0;
|
|
145726
145750
|
if (tree !== void 0) {
|
|
145727
145751
|
const fp = treeFingerprint(tree);
|
|
@@ -145730,7 +145754,11 @@ async function settleTree(env) {
|
|
|
145730
145754
|
prevTree = tree;
|
|
145731
145755
|
}
|
|
145732
145756
|
if (Date.now() >= deadline) {
|
|
145733
|
-
if (
|
|
145757
|
+
if (reads < SETTLE_MIN_READS) continue;
|
|
145758
|
+
if (prevTree === void 0 && lastError !== void 0) {
|
|
145759
|
+
if (env.treeOutage) env.treeOutage.proven = { deviceId: env.device.id, error: lastError };
|
|
145760
|
+
throw lastError;
|
|
145761
|
+
}
|
|
145734
145762
|
return prevTree;
|
|
145735
145763
|
}
|
|
145736
145764
|
if (!await sleepOrAbort(SETTLE_POLL_MS, env.signal)) return void 0;
|
|
@@ -145779,7 +145807,7 @@ async function waitForFocus(env, into, tappedFrame) {
|
|
|
145779
145807
|
for (; ; ) {
|
|
145780
145808
|
if (env.signal?.aborted) return;
|
|
145781
145809
|
try {
|
|
145782
|
-
const { tree, source } = await
|
|
145810
|
+
const { tree, source } = await readFlowTree(env);
|
|
145783
145811
|
if (!FOCUS_REPORTING_SOURCES.has(source)) return;
|
|
145784
145812
|
const target = flowSelectorToFrame(tree, into) ?? tappedFrame;
|
|
145785
145813
|
if (collectFocused(tree, []).some((n) => framesOverlap(n.frame, target))) return;
|
|
@@ -145892,6 +145920,25 @@ async function runDirective(env, step) {
|
|
|
145892
145920
|
return runRotate(env, step);
|
|
145893
145921
|
}
|
|
145894
145922
|
}
|
|
145923
|
+
async function settleForGesture(env) {
|
|
145924
|
+
let warning;
|
|
145925
|
+
if (supportsFlowTree(env.device.platform)) {
|
|
145926
|
+
try {
|
|
145927
|
+
await settleTree(env, { skipProvenOutage: true });
|
|
145928
|
+
} catch (err) {
|
|
145929
|
+
warning = unsettledGestureWarning(err);
|
|
145930
|
+
}
|
|
145931
|
+
}
|
|
145932
|
+
if (env.signal?.aborted) return { aborted: true };
|
|
145933
|
+
return warning !== void 0 ? { warning } : {};
|
|
145934
|
+
}
|
|
145935
|
+
function warned(settle) {
|
|
145936
|
+
return settle.warning !== void 0 ? { warning: settle.warning } : {};
|
|
145937
|
+
}
|
|
145938
|
+
function unsettledGestureWarning(err) {
|
|
145939
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
145940
|
+
return `dispatched without settling the screen first: the UI tree could not be read (${reason}), so there was no way to tell whether anything was moving. The gesture went out against whatever was in flight, and one aimed at a moving element can miss it entirely - this step passing says it was sent, not that it landed. Restore the tree source, or put an explicit \`wait:\` in front of gestures that follow a transition.`;
|
|
145941
|
+
}
|
|
145895
145942
|
async function resolveTargetPoint(env, target) {
|
|
145896
145943
|
if (target.selector) {
|
|
145897
145944
|
const frame = await waitForFrame(env, target.selector);
|
|
@@ -145899,26 +145946,29 @@ async function resolveTargetPoint(env, target) {
|
|
|
145899
145946
|
if (!frame) {
|
|
145900
145947
|
return { fail: { ok: false, reason: offscreenHint(target.selector) } };
|
|
145901
145948
|
}
|
|
145902
|
-
return getDescribeTapPoint(frame);
|
|
145949
|
+
return { point: getDescribeTapPoint(frame) };
|
|
145903
145950
|
}
|
|
145904
145951
|
if (typeof target.x === "number" && typeof target.y === "number") {
|
|
145905
|
-
|
|
145952
|
+
const settle = await settleForGesture(env);
|
|
145953
|
+
if (settle.aborted) return { fail: ABORTED_OUTCOME };
|
|
145954
|
+
return { point: { x: target.x, y: target.y }, ...warned(settle) };
|
|
145906
145955
|
}
|
|
145907
145956
|
return { fail: { ok: false, reason: "gesture needs a selector or x/y coordinates" } };
|
|
145908
145957
|
}
|
|
145909
145958
|
async function runTap(env, target) {
|
|
145910
|
-
const
|
|
145911
|
-
if ("fail" in
|
|
145959
|
+
const resolved = await resolveTargetPoint(env, target);
|
|
145960
|
+
if ("fail" in resolved) return resolved.fail;
|
|
145912
145961
|
await invokeOnDevice(env, "gesture-tap", {
|
|
145913
|
-
...point,
|
|
145962
|
+
...resolved.point,
|
|
145914
145963
|
...target.times !== void 0 ? { clickCount: target.times } : {}
|
|
145915
145964
|
});
|
|
145916
|
-
return { ok: true };
|
|
145965
|
+
return { ok: true, ...warned(resolved) };
|
|
145917
145966
|
}
|
|
145918
145967
|
var DEFAULT_LONG_PRESS_MS = 800;
|
|
145919
145968
|
async function runLongPress(env, step) {
|
|
145920
|
-
const
|
|
145921
|
-
if ("fail" in
|
|
145969
|
+
const resolved = await resolveTargetPoint(env, step);
|
|
145970
|
+
if ("fail" in resolved) return resolved.fail;
|
|
145971
|
+
const point = resolved.point;
|
|
145922
145972
|
const duration3 = step.duration ?? DEFAULT_LONG_PRESS_MS;
|
|
145923
145973
|
if (env.device.platform === "chromium") {
|
|
145924
145974
|
await invokeOnDevice(env, "gesture-drag", {
|
|
@@ -145936,17 +145986,21 @@ async function runLongPress(env, step) {
|
|
|
145936
145986
|
]
|
|
145937
145987
|
});
|
|
145938
145988
|
}
|
|
145939
|
-
return { ok: true };
|
|
145989
|
+
return { ok: true, ...warned(resolved) };
|
|
145940
145990
|
}
|
|
145941
145991
|
async function runPinch(env, step) {
|
|
145942
145992
|
let center = { x: 0.5, y: 0.5 };
|
|
145943
145993
|
let frame;
|
|
145994
|
+
let settle = {};
|
|
145944
145995
|
if (step.selector) {
|
|
145945
145996
|
const resolved = await waitForFrame(env, step.selector);
|
|
145946
145997
|
if (resolved === "aborted") return ABORTED_OUTCOME;
|
|
145947
145998
|
if (!resolved) return { ok: false, reason: offscreenHint(step.selector) };
|
|
145948
145999
|
frame = resolved;
|
|
145949
146000
|
center = getDescribeTapPoint(resolved);
|
|
146001
|
+
} else {
|
|
146002
|
+
settle = await settleForGesture(env);
|
|
146003
|
+
if (settle.aborted) return ABORTED_OUTCOME;
|
|
145950
146004
|
}
|
|
145951
146005
|
const { n, per } = decomposePinch(step.scale);
|
|
145952
146006
|
const guards = systemEdgeGuards(env.device);
|
|
@@ -145977,11 +146031,11 @@ async function runPinch(env, step) {
|
|
|
145977
146031
|
await invokeOnDevice(env, "gesture-pinch", args);
|
|
145978
146032
|
if (i < n - 1 && !await sleepOrAbort(PINCH_SETTLE_MS, env.signal)) return ABORTED_OUTCOME;
|
|
145979
146033
|
}
|
|
145980
|
-
return { ok: true };
|
|
146034
|
+
return { ok: true, ...warned(settle) };
|
|
145981
146035
|
}
|
|
145982
146036
|
async function fetchScreenAspect(env) {
|
|
145983
146037
|
try {
|
|
145984
|
-
const { screen } = await
|
|
146038
|
+
const { screen } = await readFlowTree(env);
|
|
145985
146039
|
return screen && screen.width > 0 && screen.height > 0 ? screen.width / screen.height : void 0;
|
|
145986
146040
|
} catch {
|
|
145987
146041
|
return void 0;
|
|
@@ -145990,12 +146044,16 @@ async function fetchScreenAspect(env) {
|
|
|
145990
146044
|
async function runRotate(env, step) {
|
|
145991
146045
|
let center = { x: 0.5, y: 0.5 };
|
|
145992
146046
|
let frame;
|
|
146047
|
+
let settle = {};
|
|
145993
146048
|
if (step.selector) {
|
|
145994
146049
|
const resolved = await waitForFrame(env, step.selector);
|
|
145995
146050
|
if (resolved === "aborted") return ABORTED_OUTCOME;
|
|
145996
146051
|
if (!resolved) return { ok: false, reason: offscreenHint(step.selector) };
|
|
145997
146052
|
frame = resolved;
|
|
145998
146053
|
center = getDescribeTapPoint(resolved);
|
|
146054
|
+
} else {
|
|
146055
|
+
settle = await settleForGesture(env);
|
|
146056
|
+
if (settle.aborted) return ABORTED_OUTCOME;
|
|
145999
146057
|
}
|
|
146000
146058
|
const aspect = await fetchScreenAspect(env);
|
|
146001
146059
|
const guards = systemEdgeGuards(env.device);
|
|
@@ -146037,7 +146095,7 @@ async function runRotate(env, step) {
|
|
|
146037
146095
|
if (env.signal?.aborted) return ABORTED_OUTCOME;
|
|
146038
146096
|
throw err;
|
|
146039
146097
|
}
|
|
146040
|
-
return { ok: true };
|
|
146098
|
+
return { ok: true, ...warned(settle) };
|
|
146041
146099
|
}
|
|
146042
146100
|
async function runType(env, step) {
|
|
146043
146101
|
const frame = await waitForFrame(env, step.into);
|
|
@@ -146069,7 +146127,7 @@ async function waitForCondition(env, step, timeoutMs) {
|
|
|
146069
146127
|
for (; ; ) {
|
|
146070
146128
|
if (env.signal?.aborted) return ABORTED_OUTCOME;
|
|
146071
146129
|
try {
|
|
146072
|
-
const data = await
|
|
146130
|
+
const data = await readFlowTree(env);
|
|
146073
146131
|
lastMatches = flowFindAll(data.tree, step.selector);
|
|
146074
146132
|
fetchError = void 0;
|
|
146075
146133
|
everMatched ||= lastMatches.length > 0;
|
|
@@ -146164,11 +146222,7 @@ async function waitForIdle(env, step) {
|
|
|
146164
146222
|
const roundStartedAt = Date.now();
|
|
146165
146223
|
let answeredReadMs;
|
|
146166
146224
|
const [read, frame] = await Promise.all([
|
|
146167
|
-
settleWithin(
|
|
146168
|
-
fetchFlowTree(env.registry, env.device, env.launchedNativeApp),
|
|
146169
|
-
roundBudget,
|
|
146170
|
-
env.signal
|
|
146171
|
-
).then((r) => {
|
|
146225
|
+
settleWithin(readFlowTree(env), roundBudget, env.signal).then((r) => {
|
|
146172
146226
|
answeredReadMs = Date.now() - roundStartedAt;
|
|
146173
146227
|
return r;
|
|
146174
146228
|
}),
|
|
@@ -148807,6 +148861,7 @@ async function treeSourceGate(registry2, device, bundleId, signal) {
|
|
|
148807
148861
|
async function runLaunch(state3, app) {
|
|
148808
148862
|
const env = deviceEnv(state3);
|
|
148809
148863
|
const { registry: registry2, device, signal } = env;
|
|
148864
|
+
if (state3.treeOutage) state3.treeOutage.proven = void 0;
|
|
148810
148865
|
if (device.platform === "chromium") return runChromiumLaunch(state3, app);
|
|
148811
148866
|
const bundleId = appIdForPlatform(app, device.platform);
|
|
148812
148867
|
if (!bundleId) {
|
|
@@ -149028,8 +149083,8 @@ when \`on\` is omitted; distinct from the \`rotate\` tool, which changes device
|
|
|
149028
149083
|
for a UI condition, and additionally takes the one condition that has no selector: \`idle: true\` waits
|
|
149029
149084
|
until the screen has content and stops moving in BOTH the UI tree and the rendered pixels (it never
|
|
149030
149085
|
fails a run \u2014 a screen that never settles passes carrying a \`warning\`, which is what makes it safe to
|
|
149031
|
-
persist; the one outcome that does stop the run is an \`error\` for a tree source
|
|
149032
|
-
at all \u2014 a broken window rather than a verdict about the app, which leaves the run not-ok and skips
|
|
149086
|
+
persist; the one idle outcome that does stop the run is an \`error\` for a tree source THIS step could not
|
|
149087
|
+
read at all \u2014 a broken window rather than a verdict about the app, which leaves the run not-ok and skips
|
|
149033
149088
|
every later step; it says nothing about WHICH screen settled \u2014 a dropped tap leaves the source screen
|
|
149034
149089
|
perfectly idle \u2014 so pair it with the element check that names the destination); \`wait\` pauses for a fixed number of milliseconds; \`assert\` checks one now; \`snapshot\`
|
|
149035
149090
|
diffs a screenshot \u2014 or, with \`cropOn: <selector>\`, one element's cropped region \u2014 against a stored
|
|
@@ -149037,6 +149092,14 @@ baseline (a missing baseline fails the step \u2014 set updateBaselines to adopt
|
|
|
149037
149092
|
cropped element whose size drifted fails on dimensions); \`echo\` annotates; \`run\` executes another flow
|
|
149038
149093
|
inline \u2014 a YAML path resolved against the directory of the flow file that references it (co-located
|
|
149039
149094
|
runs only).
|
|
149095
|
+
A selector-less gesture \u2014 a coordinate \`tap\`/\`long-press\`, or a \`pinch\`/\`rotate\` with no \`on\` \u2014 resolves
|
|
149096
|
+
no frame out of the tree, so an unreadable tree source does NOT stop it the way it stops \`idle\`: it
|
|
149097
|
+
settles best-effort, dispatches anyway, and the step PASSES carrying a \`warning\` that quotes the source's
|
|
149098
|
+
own error. That green says the gesture was SENT, not that it landed. Restore the tree source (usually
|
|
149099
|
+
relaunch the app so the instrumentation loads), or accept the warning where the app can serve no tree;
|
|
149100
|
+
the first such gesture proves the outage and later ones spend that verdict without paying the settle
|
|
149101
|
+
window again. A tree read that comes back, or a relaunch, retires that verdict \u2014 which only makes the
|
|
149102
|
+
next gesture pay a fresh window, and it warns again if the source is still down.
|
|
149040
149103
|
A \`when:\` block (condition + \`steps:\`, no else) runs its steps only if the condition holds \u2014
|
|
149041
149104
|
checked once with the short assert grace \u2014 for one-sided divergences like interstitials and coach
|
|
149042
149105
|
marks; a skipped block reports distinctly and failures inside an entered block are real failures.
|
|
@@ -149118,6 +149181,12 @@ Pass exactly one flow source: name for a saved flow under project_root, or flow_
|
|
|
149118
149181
|
device,
|
|
149119
149182
|
deviceIsExplicit: Boolean(params.device),
|
|
149120
149183
|
signal,
|
|
149184
|
+
// One holder per ExecState, shared by nested `run:` flows: `deviceEnv`
|
|
149185
|
+
// spreads the reference, so what one step's settle learns about the
|
|
149186
|
+
// tree source the next one already has. A `tool: flow-execute` builds
|
|
149187
|
+
// its own instead, which is why that step spends this verdict rather
|
|
149188
|
+
// than inheriting whatever the sub-run proved.
|
|
149189
|
+
treeOutage: {},
|
|
149121
149190
|
flowsDir,
|
|
149122
149191
|
viaUpload,
|
|
149123
149192
|
baselineKey: baselineKeyFor(canonicalPath, flowName),
|
|
@@ -149711,6 +149780,10 @@ async function execLeafStep(state3, step, index, scope) {
|
|
|
149711
149780
|
try {
|
|
149712
149781
|
if (FOREGROUND_CHANGING_TOOLS.has(step.name)) {
|
|
149713
149782
|
state3.launchedNativeApp = void 0;
|
|
149783
|
+
if (state3.treeOutage) state3.treeOutage.proven = void 0;
|
|
149784
|
+
}
|
|
149785
|
+
if (isNestedOrchestratorTool(step.name) && state3.treeOutage) {
|
|
149786
|
+
state3.treeOutage.proven = void 0;
|
|
149714
149787
|
}
|
|
149715
149788
|
const result = await invokeSubTool(registry2, ctx, step.name, args);
|
|
149716
149789
|
if (isUnmetUiWaitResult(step.name, result)) {
|
package/package.json
CHANGED
|
@@ -112,6 +112,8 @@ In a `scroll-to` map, put the selector under `target:`. The map supports `up`, `
|
|
|
112
112
|
|
|
113
113
|
`type` presses Enter unless `submit: false`. A polished focus tap plus keyboard call usually needs `submit: false`. Store external values as `{{secret:NAME}}`. The runner uses the first source that defines the name: environment `ARGENT_SECRET_NAME`; project `.argent/secrets.env`; project `.env.local`, then `.env`; then `~/.argent/secrets.env`. The two `secrets.env` files accept the bare `NAME`, but the shared dotenv files expose only `ARGENT_SECRET_`-prefixed keys, so a bare `NAME=…` in `.env` or `.env.local` stays unresolved. The runner redacts every resolved value, so do not use a placeholder for content a report must show.
|
|
114
114
|
|
|
115
|
+
A **selector-less gesture** — a coordinate `tap`/`long-press`, or a `pinch`/`rotate` with no `on:` — resolves no frame, so a tree source it cannot read does not fail it. It settles best effort, dispatches anyway, and the step **passes carrying a warning** that quotes the source's own error. That green says the gesture was sent, not that it landed: one aimed at a moving element can miss it entirely. Restore the tree source, usually by relaunching the app so the instrumentation loads. Accept the warning only where the app serves no tree at all, and put an explicit `wait:` before a gesture that follows a transition. The first such gesture proves the outage and later ones spend that verdict without paying the settle window again. A tree read that comes back, or a relaunch, retires that verdict — which only makes the next gesture pay a fresh window, and it warns again if the source is still down.
|
|
116
|
+
|
|
115
117
|
## Verification conditions
|
|
116
118
|
|
|
117
119
|
```yaml
|
|
@@ -164,7 +166,7 @@ It **never fails a run.** Every outcome short of a clean settle passes with a wa
|
|
|
164
166
|
- **settled on the UI tree alone** — no screenshot pair could be read, so presentation-layer animation was never waited out.
|
|
165
167
|
- **too few reads** — a settle needs three reads across two intervals and this step got fewer, so it ended with no evidence either way.
|
|
166
168
|
|
|
167
|
-
Only a tree source
|
|
169
|
+
Only a tree source this step could not read stops the run, as an errored step — one still failing when the wait ends, one that wedges after answering, one that answers with an empty tree it flags as degraded (an unattached Vega toolkit, an AX service asking to be relaunched), or one that never answers (raise `timeout` before suspecting the app). The run is then not ok and every later step is skipped. A single failed read is not that: the hold restarts from the next good read. The same outage stops no [selector-less gesture](#directives), which needs no frame and passes with its own warning instead.
|
|
168
170
|
|
|
169
171
|
`idle` proves readiness only and never identifies the screen, so it cannot serve as acceptance evidence or replace the identity gate. Gate the next action on a stable element. Add `idle` during polish after each screen change, not after every step.
|
|
170
172
|
|
|
@@ -239,4 +239,6 @@ Manual rescue invalidates the pass. An `errored` step was never evaluated: an `i
|
|
|
239
239
|
|
|
240
240
|
**A passing step that carries a `warning` is a finding, not noise.** `await: { idle: true }` raises [six different warnings](flow-yaml.md#idle-readiness) and they do not share one meaning. Two say the screen was moving; one says the wait ran out mid-hold and is repaired by raising the step's `timeout:`; one says the tree stayed empty; one says the tree did hold still and only the screenshot pairs were missing, so the capture path is what to check; one says the step ended with no evidence either way. No report separates intended motion from a load that never finished. Read which one it is, look at that screen, disclose what you found, and confirm the following step targets a stable element rather than stillness.
|
|
241
241
|
|
|
242
|
+
A [selector-less gesture](flow-yaml.md#directives) raises a warning of a different shape, not one of those six: a tree-source outage left it unsettled, so it dispatched blind and the green says only that the gesture was sent. Restore the source, usually by relaunching the app so the instrumentation loads. Accept it only where the app serves no tree at all, such as the [injection-free iOS form](reliability-and-recovery.md#terminally-non-injectable-ios-apps).
|
|
243
|
+
|
|
242
244
|
One uninterrupted full pass completes a normal flow. `argent-qa-flows` requires two consecutive passes of unchanged YAML. For CI, use `argent flow run <name> [--platform ...]`; it exits non-zero on failure.
|
|
@@ -62,6 +62,8 @@ Apple system apps cannot load the instrumentation, and nothing in the launch pat
|
|
|
62
62
|
- A point focus tap plus raw keyboard with `delayMs: 500`.
|
|
63
63
|
- Raw swipes with `settle: true` because `scroll-to` needs the missing flow tree. Momentum-free scrolling keeps later coordinate taps valid.
|
|
64
64
|
|
|
65
|
+
Every point tap or long-press in such a flow passes **carrying a warning**. The app loads no instrumentation, so every tree read fails and each [selector-less gesture](flow-yaml.md#directives) dispatches unsettled. Nothing here repairs it. Accept the warnings, read each green as "the gesture was sent, not that it landed", and put an explicit `wait:` or a raw `tool: await-ui-element` before a gesture that follows a transition. Raw `tool:` steps never take that settle, so they never warn.
|
|
66
|
+
|
|
65
67
|
Report that the flow is injection-free and its coordinates are not portable. It cannot satisfy the QA contract. Report the artifact and platform blocker instead.
|
|
66
68
|
|
|
67
69
|
The same fragment fallback covers a normally injectable app that is broken in the environment: raw `restart-app` in place of `launch:` still makes a self-resetting flow. Either way it is not e2e and cannot complete `argent-qa-flows`, which requires a leading `launch:`. Report the blocker rather than labeling that fallback a completed QA test.
|
|
@@ -111,6 +113,7 @@ Classify before editing:
|
|
|
111
113
|
| Partial divergence | An intermediate result disagrees with its echo | Find the first divergent transition |
|
|
112
114
|
| Acceptance failure | Actions pass but a requested check fails | Preserve the check and investigate behavior |
|
|
113
115
|
| Idle warning | A readiness step passes without settling | Read [which of the six warnings](flow-yaml.md#idle-readiness) it is, then gate the next action on a stable element |
|
|
116
|
+
| Unsettled gesture | A selector-less gesture passes unsettled | Restore the tree source, usually by relaunching the app; the green says [only that the gesture was sent](flow-yaml.md#directives) |
|
|
114
117
|
|
|
115
118
|
Then:
|
|
116
119
|
|
|
@@ -101,7 +101,7 @@ After the last edit and audit, set the streak to zero:
|
|
|
101
101
|
2. Seed, review, and freeze snapshot baselines. Baseline updates do not count as passes.
|
|
102
102
|
3. Before mobile pass 1, recycle Argent services for this flow's device: two warm passes are correlated evidence, because a fixed timing margin can pass twice simply because environment speed did not change. Scope `stop-all-simulator-servers` to `devices: [<device>]`. Never omit the scope — a bare call is the machine-wide sweep, and step 7 restarts this proof often enough to reap every other agent's devices repeatedly. Use the MCP call for `flow-execute`, or `argent run stop-all-simulator-servers --devices <device>` from the standalone runner's install. The reset must not change app or account data. For Chromium, let the runner boot the declared app and omit `device`. Vega owns no recyclable Argent services, so the teardown is a no-op there and both passes are warm.
|
|
103
103
|
4. Run from the flow's launch and setup without baseline-update mode. Count a pass only when `ok: true` and every acceptance check executed. A false `when:` can skip optional setup only. An errored step does not advance the streak, and the count mixes two kinds — read each reason. One that could not run (an unreadable tree under `idle`, an unresolvable `run:` target) is environment: fix it and rerun. **A failed `launch:` also scores `errored`, and it is a verdict about the app** — an app that no longer installs or starts is the regression this test exists to catch, so report it instead of rerunning.
|
|
104
|
-
5. Resolve every passing-step warning before completion. `await: { idle: true }` raises [six different warnings](../argent-create-flow/references/flow-yaml.md#idle-readiness), so read which one it is first. Two say the screen was moving. One says the wait ran out mid-hold and needs a larger `timeout:`. One says the tree stayed empty. One — **settled on the UI tree alone** — says the hierarchy did hold still and only the screenshot pairs were missing, so inspect the capture path rather than the app's rendering. One says the step ended with no evidence either way. Inspect the screen, disclose the cause, and verify that surrounding acceptance checks use stable elements rather than stillness.
|
|
104
|
+
5. Resolve every passing-step warning before completion. `await: { idle: true }` raises [six different warnings](../argent-create-flow/references/flow-yaml.md#idle-readiness), so read which one it is first. Two say the screen was moving. One says the wait ran out mid-hold and needs a larger `timeout:`. One says the tree stayed empty. One — **settled on the UI tree alone** — says the hierarchy did hold still and only the screenshot pairs were missing, so inspect the capture path rather than the app's rendering. One says the step ended with no evidence either way. Inspect the screen, disclose the cause, and verify that surrounding acceptance checks use stable elements rather than stillness. A [selector-less gesture](../argent-create-flow/references/flow-yaml.md#directives) — a coordinate `tap`/`long-press`, or a `pinch`/`rotate` with no `on:` — warns in a different shape: a tree-source outage left it unsettled, so it dispatched blind and the green says only that the gesture was sent. Restore the tree source, usually by relaunching the app so the instrumentation loads, and rerun. Accepting that warning needs an app that serves no tree, which cannot satisfy this contract anyway.
|
|
105
105
|
6. Run the same YAML again immediately with the same runner. Do not manually reset app or account data.
|
|
106
106
|
7. Reset the streak after any failure, edit, re-recording, baseline update, or state-changing manual recovery. Repair through `argent-create-flow`, audit again, and restart with fresh services.
|
|
107
107
|
|