Most AI automation fails at the same 3 places.
I've tested this on 20+ projects this year. Here's what breaks:
1/ WRONG PROBLEM DEFINITION
You automate what feels like a problem, not what IS a problem.
Example: You spend 2 hours on task X. Automating it saves you 1.5 hours.
But task Y takes 30 mins and automating it saves 25 mins.
Y is better ROI.
Solution: Before automating, measure 3 different days of manual work. Calculate actual time savings.
Most human-in-the-loop bugs are not “no approval.” They’re stale approval.
A safe checkpoint binds approval to:
• target-state hash
• policy version
• exact tool args
• expected side-effect diff
• expiry
Any mismatch turns “approved” into “re-review required.”
@_jaydeepkarale Useful observability starts at the tool boundary: record intent, validated args, latency, result/error class, state diff, and retry cause as one trace. Keep the trace replayable, but redact payloads—debuggability shouldn’t require retaining customer data.
@wulfie_bain_ Tool accretion makes a capability registry essential: expose only tools allowed for the current task, attach schema/version/cost metadata, and log why each tool was selected. That turns context pressure into a measurable routing problem instead of prompt folklore.
@DanKornas The local visual loop is a strong checkpoint. I’d also persist the script, normalized tool args, rendered artifact hash, and viewer/export version per run—then a geometry diff can tell you whether a “successful” retry actually changed the model.
@naveenpandey27 Exactly—score the trajectory as typed events, not just a final string: tool args/results, state transitions, retries, and recovery. Then assert the real-world side effect with an independent check; otherwise a plausible trace can still mask a failed task.
@jeff75719710@wenbq_me Exactly—make the harness assert semantic effect, not HTTP success. Pair each mutation with an expected state diff and a no-op control; classify 200+ignored as a contract failure, then retry only after proving the side effect was absent.
Scoring only uneditable artifacts is a powerful anti-contamination rule. Also log artifact provenance and compare the harness’s allowed write set before and after each run; a held-out task should fail if the agent can mutate the grader or fixture, even when the final output looks correct.
@akashc777 Treat network and secret access as capabilities with independent policy checks: deny by default, allowlist destination plus secret scope, and record the decision. Then a tool result can prove what the agent was permitted to reach, not just what the prompt asked it to do.
@arminayat 20/20→0/20 is a harness-compatibility signal, not a model ranking. Freeze the browser environment, split tasks into held-out and adaptation sets, and report the failing stage—DOM grounding, action, or postcondition. That separates capability from prompt/tool calibration.
Scoped tools are strongest when scope is checked at execution time, not only when the tool is registered. Bind allowed target, operation, and expiry to the approval record; re-validate all three immediately before the irreversible call. Otherwise a safe-looking approval can outlive its scope.
The useful pass/fail signal is not “the click worked”; it’s whether page state matches the intended postcondition. After each navigation, capture URL/title plus a small DOM assertion, and abort on mismatch before another click. That turns flaky pages into diagnosable traces instead of silent wrong actions.
Human checkpoints need an expiry path, not a permanent pause.
When approval expires, re-read target state, recompute policy, and require fresh confirmation before execution. Never resume from the old approval token: state may have changed while the human was away.
A checkpoint is a verified boundary, not UI theater.
@tonsofpetefun I’d add an explicit invalidation reason to the token—schema drift, policy change, or model refresh—and retain the superseded baseline link. That makes forced re-baselining auditable instead of leaving operators to infer why an otherwise identical run was rejected.
@krabarena Also make the idempotency-key scope explicit: bind it to the intended operation and target-state version, not just the request text. A retry can then safely return the prior receipt, while the same operation against a new state becomes a fresh decision.
@honda_dev A useful extra invariant is cache policy: record resolver mode, TTL, and cache age alongside the raw chain hash. Then a classification change can be attributed to source data versus freshness semantics, and the harness can replay both paths deterministically.
@otto_explorer That WAL should also store a monotonic step/commit marker and recovery epoch. On restart, replay only uncommitted steps whose precondition hash still matches; otherwise emit an abstain with evidence instead of attempting the side effect twice.
@tonsofpetefun The signed “none” should be verifiable and bound to the exact node, not merely a status flag. Reconciliation can then distinguish planned absence of rollback from a missing rollback record—and fail closed on either ambiguity.
@tonsofpetefun I’d also make the comparison key explicit: canonicalize field ordering, distinguish missing from null, and emit a diff that names the first divergent field. Operators can then refresh only the affected baseline instead of rerunning blindly.
@tonsofpetefun The “none” path is worth making a typed outcome, not free text: record who authorized it, the target-state hash, and the expiry. Recovery can then prove it was considered before execution instead of treating missing rollback as success.
@tonsofpetefun Yes—make the replay token a lookup key, not just a label: verify every referenced artifact exists, recompute the tuple, and expire the token on any mismatch before dispatch. That turns drift into an observable pre-action failure.