批量设备任务显示 COMPLETED,为什么还有设备失败?
用户对语音 Agent 说:“把会议室三盏灯都关掉。”后台批量任务显示 COMPLETED,Agent 回答“都关好了”,现场却还有一盏亮着。这是教学案例,没有真实灯具或 AWS 账号实测。它暴露的工程问题是:批次结束、设备上报成功、物理状态已核实,是三件不同的事。
AWS IoT Jobs 生命周期文档给出一个明确反例:对 snapshot job,所有 job execution 到达终态后,整体 job 可转为 COMPLETED;这些终态可以包含 FAILED、TIMED_OUT、REMOVED 和 CANCELED,并非全是 SUCCEEDED。因此,读到整体 COMPLETED,只能先说“这批执行已收尾”,不能向用户承诺“每台设备都成功”。这里讨论的是 AWS IoT Jobs,不是另一套 AWS IoT Commands API。Jobs 文档将 execution 的 TIMED_OUT 列为终态;不能套用 Commands 云端超时可被晚到回报覆盖的规则。文档于 2026 年 10 月 13 日再次核实,正文的整体收尾结论只限定在 snapshot job。
先从批次状态下钻到每台设备
AWS 将一个 job 与每个目标设备上的 job execution 分开。CLI 文档中的 ListJobExecutionsForJob 可列出设备及其 execution 状态;需要定位一台设备时,用 DescribeJobExecution 查其详情。JobProcessDetails API还给出成功、失败、超时、取消等分类计数。计数适合发现异常,逐设备记录才能回答“究竟哪一台需要处理”。列表有分页时须遍历全部页。
假设本次目标清单在下发前固定为 A、B、C 三台灯;最终 A 上报 SUCCEEDED,B 上报 FAILED,C 记录为 TIMED_OUT。三个 execution 都可进入终态,整体 job 因而显示 COMPLETED。Agent 的第一句回复应是:“任务批次已结束;A 上报成功,B 上报失败,C 超时。三台灯的实际状态仍待核实。”A/B/C 是教学演算,不是平台运行记录。
SUCCEEDED 也有证据边界。AWS 设备工作流文档描述设备完成操作后调用 UpdateJobExecution 报告成功。平台接收的是设备上报的执行状态;如果业务要确认灯是否真的熄灭,还需要可信的设备状态或传感回报。没有这种证据,就不能把 A 的 SUCCEEDED 扩写为“灯已经关好”。
计数能发现问题,设备清单才能定位问题
设想平台给出成功一台、失败一台、超时一台,合计三台。这个计数说明有异常,却还不能告诉用户该去哪个房间,也不能指导下一次只重试哪盏灯。必须把计数与固定目标清单和逐设备 execution 对齐,确认 A、B、C 都有结果,没有重复身份或漏项。计数相等只是一个机械检查,不是动作验收。
列表若有分页,第二页可能正好包含失败设备。读取第一页就宣布全部成功,不只是省略信息,而是改变了目标集合。查询过程保存是否完整、每页返回的查询边界、最终覆盖的设备集合;遇到分页失败或权限限制,把缺失项明确列为未查询,不能当作没有异常。这里没有规定 API 的分页实现,只要求按当前接口完整遍历。
平台汇总与逐设备记录也可能不是同一时刻的快照。查询过程中一台设备回报改变,计数与列表暂时不一致,先保留各次查询时间和原始结果,再按接口实际能力复查。不要为让表格合计一致而补造某个状态。应用结果应注明观察时间,而不是暗示所有设备在同一物理瞬间被确认。
目标设备自身也需要稳定身份。用户说会议室灯,设备显示名相似或后来改名时,查询键不应只靠名称字符串匹配。下发时确定具体设备身份与用户可读名称,结果回传按身份关联,再给用户可辨认的说明。设备组里新增或移除成员,也不能静默改写这次已有批次的目标。
若某台目标根本没有对应 execution,应用要保留“未创建执行”这一类事实。它可能源于提交失败、权限、能力不匹配或查询不完整,不能直接混成设备执行失败。先核对提交阶段与查询覆盖,才能决定下一步补提交还是补证据。批次结束并不会自动让这些遗漏消失。
把一次用户意图拆成逐设备结果
应用侧可以保存一份逐设备记录:batch_id、下发时的目标设备集合、每台的 execution 查询键、期望动作、截止时间、最后一次平台上报,以及最后一次可信物理状态。这是作者提出的系统设计,不是 AWS Jobs 自动替 Agent 维护的账本。目标集合必须在下发时确定;事后重新查询动态设备组,可能把新加入的设备算进去,也可能遗漏当时的目标。
聚合话术要按用户目标的证据计算。三台都有可信结果证明灯已关闭,才说“全部完成”;A 已核实关闭、B 已核实未关闭,才说“部分完成”;还有无法核实的设备,就点名未知项。一个业务目标即使必须“三台都关”,失败时也不能抹掉某台已经上报或已核实的事实,否则恢复流程会再作用到它。
业务结果不等于平台状态的翻译
可以把每台结果分成几列:目标是什么、平台最近上报是什么、可信状态观察是什么、当前是否满足目标、旧执行是否还可能动作。平台状态照原样保留,业务判断基于这些列生成。这样一台上报失败但当前已关灯的设备,可以报告目标当前满足、执行失败仍需诊断,而不是强行改成成功。
同样,一台上报成功却仍报告开灯,要保留冲突并查来源。成功可能描述软件流程完成,状态可能来自旧采样,也可能确实存在设备故障。没有补到关联与新鲜度证据前,不能让某个字段覆盖另一个字段。这个反例是待验证设计,不声称任何厂商实现一定存在这种问题。
聚合时,先问用户目标是否要求全体达成。“三盏都关”是全部设备当前状态满足才算达成;若目标是“尽量关闭能连接的灯”,产品可以允许部分完成,但仍需要说明无法确认的设备。成功标准应在下发前确定,不能在看到失败后临时改成“大多数成功也算完成”。
未知也有不同来源。未查询、查询失败、设备超时、状态过旧和执行与状态冲突,都可以导致暂时不能确认,但对应下一步不同。用户第一句反馈无需列尽所有内部类别,可以点名仍未确认的设备;后台保留具体原因,恢复时再选择查询、等待、诊断或人工观察。
还要区分历史批次与当前状态。批次昨天已经收尾,今天灯可能被手动打开;历史执行成功不能证明当前仍关闭。用户追问“现在都关了吗”,应重新获得当前观察,而不是复述历史 COMPLETED。若本次目标只要求在某时刻完成,也应说明确认发生的时间,避免状态持续性被暗中承诺。
恢复按设备做,不重跑整批
B 报告失败,不等于可以把整批再次执行。A 可能已经关灯,重发会造成重复动作;C 超时不能证明设备此前没有动作,也不能据此认定当前物理状态。先查 B、C 的具体 execution 和可信设备状态;只有确认旧执行以后不会再动作、该设备未达目标,才结合动作风险决定是否新建执行。对状态未知的设备,先报告未知并暂停自动补发。
若业务要求“要么都关,要么都不关”,并且已核实 A 关灯、B 无法关灯,想把 A 再打开属于新的设备动作,可能失败;它不是数据库事务式自动回滚。灯具可以由用户选择接受部分结果或人工处理;门禁、电机等高风险设备应在结果未核实前停下自动补发和自动补偿。
复制运行:分别算“收尾”与“达成目标”
下面用标准库把上述聚合规则做成可运行例子。输入是构造的三设备快照,不是 AWS API 响应;目标为 A、B、C 全部关闭。下发时固定的设备与 execution 对应关系放在 TARGETS,函数不重新查询动态设备组,新增设备 D 不会进入原目标的分母。这里的 execution 字符串是教学用查询关联键,不声称 AWS Jobs 存在同名的通用字段。
reports 保存平台执行记录,observations 单独保存模拟物理观察。只有批次、执行键、声明的可信来源与时间窗都符合本例约定,观察才可用于判断灯是否已关闭。时间使用整数演算:当前为 100,接受 90 到 100 的观察;这个十单位窗口只是例子中的业务输入条件,不是厂商推荐阈值,也没有测真实设备时钟同步。
保存为 batch_result.py,使用 Python 3.8 或以上运行。仅有标准库,不用 AWS SDK,不配置账号。自检依赖 assert,不要用 python -O。
python3 batch_result.py
"""Fixed three-device snapshot demo; constructed data, no AWS or device calls."""
from copy import deepcopy
TERMINAL = {"SUCCEEDED", "FAILED", "TIMED_OUT", "REJECTED", "REMOVED", "CANCELED"}
TARGETS = {"A": "exec-A", "B": "exec-B", "C": "exec-C"}
def summarize(batch, targets, reports, observations, now=100):
if not batch or not targets:
raise ValueError("empty batch or targets")
accepted = {}
ignored = 0
for row in reports:
device = row["device"]
if (row["batch"] != batch or device not in targets
or row["execution"] != targets[device]):
ignored += 1
continue
if device in accepted:
raise ValueError("duplicate current-device report")
if row["status"] not in TERMINAL | {"QUEUED", "IN_PROGRESS"}:
raise ValueError("unknown execution status")
accepted[device] = row
met, unmet, unknown, retry, diagnose = [], [], [], [], []
for device, execution in targets.items():
report = accepted.get(device)
obs = observations.get(device)
valid = (obs is not None and obs["batch"] == batch
and obs["execution"] == execution
and obs["source"] == "trusted_sensor"
and now - 10 <= obs["time"] <= now
and type(obs["off"]) is bool)
if not valid:
unknown.append(device)
elif obs["off"]:
met.append(device)
else:
unmet.append(device)
if report and report["status"] == "SUCCEEDED":
diagnose.append(device)
elif (report and report["status"] in TERMINAL
and report.get("old_stopped") is True):
retry.append(device)
goal = ("UNKNOWN" if unknown else "ALL_CONFIRMED" if len(met) == len(targets)
else "PARTIAL" if met else "NONE_CONFIRMED")
ended = (len(accepted) == len(targets)
and all(r["status"] in TERMINAL for r in accepted.values()))
return {"execution_ended": ended, "goal": goal, "met": met, "unmet": unmet,
"unknown": unknown, "retry_candidates": retry, "diagnose": diagnose,
"reports": {d: r["status"] for d, r in accepted.items()},
"missing_reports": [d for d in targets if d not in accepted],
"ignored_reports": ignored}
def report(device, status):
return {"batch": "batch-1", "device": device, "execution": TARGETS[device],
"status": status, "old_stopped": True}
def observation(device, off):
return {"batch": "batch-1", "execution": TARGETS[device],
"source": "trusted_sensor", "time": 100, "off": off}
def main():
rows = [report("A", "SUCCEEDED"), report("B", "FAILED"), report("C", "TIMED_OUT")]
obs = {"A": observation("A", True), "B": observation("B", False)}
result = summarize("batch-1", TARGETS, rows, obs)
assert result["execution_ended"] and result["goal"] == "UNKNOWN"
assert result["retry_candidates"] == ["B"] and result["unknown"] == ["C"]
print("terminal batch: goal=UNKNOWN retry_candidates=['B'] query=['C']")
all_off = {d: observation(d, True) for d in TARGETS}
result = summarize("batch-1", TARGETS, rows, all_off)
assert result["goal"] == "ALL_CONFIRMED" and result["reports"]["B"] == "FAILED"
assert result["retry_candidates"] == []
print("current goal met: ALL_CONFIRMED B_report=FAILED retry_candidates=[]")
mixed = deepcopy(all_off)
mixed["B"]["off"] = False
result = summarize("batch-1", TARGETS, rows, mixed)
assert result["goal"] == "PARTIAL" and result["retry_candidates"] == ["B"]
print("partial observation: met=['A', 'C'] unmet=['B']")
blocked = deepcopy(rows)
blocked[1]["old_stopped"] = False
assert summarize("batch-1", TARGETS, blocked, mixed)["retry_candidates"] == []
print("old execution not stopped: retry_candidates=[]")
success = [report(d, "SUCCEEDED") for d in TARGETS]
assert summarize("batch-1", TARGETS, success, obs)["goal"] == "UNKNOWN"
print("all reported success/missing C observation: UNKNOWN")
wrong = report("C", "SUCCEEDED")
wrong["batch"] = "batch-old"
missing = summarize("batch-1", TARGETS, rows[:2] + [wrong], obs)
assert missing["missing_reports"] == ["C"] and not missing["execution_ended"]
print("old batch excluded: missing_reports=['C'] execution_ended=False")
wrong["batch"] = "batch-1"
wrong["execution"] = "exec-C-old"
assert summarize("batch-1", TARGETS, rows[:2] + [wrong], obs)["missing_reports"] == ["C"]
extra = dict(report("A", "SUCCEEDED"), device="D", execution="exec-D")
assert summarize("batch-1", TARGETS, rows + [extra], obs)["ignored_reports"] == 1
print("wrong execution and extra D: excluded from fixed targets")
try:
summarize("batch-1", TARGETS, rows + [rows[0]], obs)
except ValueError:
pass
else:
raise AssertionError("duplicate report accepted")
print("duplicate current-device report: REJECTED")
stale = deepcopy(all_off)
stale["C"]["time"] = 89
assert summarize("batch-1", TARGETS, rows, stale)["unknown"] == ["C"]
stale["C"]["time"] = 101
assert summarize("batch-1", TARGETS, rows, stale)["unknown"] == ["C"]
stale["C"]["time"] = 100
stale["C"]["source"] = "device_claim"
assert summarize("batch-1", TARGETS, rows, stale)["unknown"] == ["C"]
print("stale/future/untrusted observation: C=UNKNOWN")
conflict = summarize("batch-1", TARGETS, success, mixed)
assert conflict["diagnose"] == ["B"] and conflict["retry_candidates"] == []
print("SUCCEEDED but observed on: diagnose=['B'] retry_candidates=[]")
print("PASS: fixed-target and evidence assertions")
if __name__ == "__main__":
main()
2026 年 10 月 13 日本地实际运行,退出码 0,标准输出为:
terminal batch: goal=UNKNOWN retry_candidates=['B'] query=['C']
current goal met: ALL_CONFIRMED B_report=FAILED retry_candidates=[]
partial observation: met=['A', 'C'] unmet=['B']
old execution not stopped: retry_candidates=[]
all reported success/missing C observation: UNKNOWN
old batch excluded: missing_reports=['C'] execution_ended=False
wrong execution and extra D: excluded from fixed targets
duplicate current-device report: REJECTED
stale/future/untrusted observation: C=UNKNOWN
SUCCEEDED but observed on: diagnose=['B'] retry_candidates=[]
PASS: fixed-target and evidence assertions
第一行把三种终态放在同一批次:A 成功、B 失败、C 超时,execution_ended 为真;但 A 的观察为已关,B 为仍开,C 没有物理观察,因此业务结果是 UNKNOWN。不是多数成功就说“完成”,也不是已有失败就抹掉未知。B 进入恢复候选,C 进入查询集合,二者的动作不同。
第二行给三台都补上当前已关的模拟观察,业务结果变成 ALL_CONFIRMED;B 的原 FAILED 仍保留,恢复候选为空。这表达的是当前关闭目标已满足、B 的历史执行仍报告失败,并不把 B 的执行状态改成成功。平台失败可能需要诊断,但不需要为了让历史字段变绿而再向已达目标的设备发动作。反过来,这些构造观察也不证明设备曾完成哪段执行流程,只判定输入中当前目标状态。
第三行把 B 的观察改为仍开,A 和 C 保持已关;目标结果是 PARTIAL,未达成名单只有 B。与第一行相比,变化仅是 C 从缺证据变成已核实;未知消失后,才可以用“部分达成”完整描述这组输入。用户能据此判断剩下哪台需要处理,而不必猜部分成功是否还藏着离线设备。
恢复候选不是自动重发命令
代码中的 old_stopped 是独立给入的构造证据,表示旧执行此后不会再启动或继续动作。它不是读取到终态后自动设置的标志,也不是 AWS API 的同名保证。把 B 的这个字段改成假,仍然能知道 B 当前没关,却不再产生恢复候选;代码用这一反例检查“目标未达成”与“现在可以再次作用设备”是两个判断。
即便 B 出现在 retry_candidates,本例也没有发送请求。实际恢复还要检查当前用户意图、动作风险、有效期与设备协议能力,再决定是否建立新的执行。对 C,缺少物理结果就先查;对 A,目标已满足就保留原结果。聚合函数交付的是有理由的候选集合,不能直接拿它驱动所有设备的自动重试。
当平台报告 SUCCEEDED 而可信模拟观察为仍开,本例把 B 放入 diagnose,不放入恢复候选。当前目标没有达成已经可见,但执行与观察为何冲突仍不清楚。可能需要核对观察与执行的关联、设备上报语义,或是否有后续操作;保留两列原始证据比选择一个字段覆盖另一列更利于排查。这里展示输入冲突的应用处理,不指称 AWS 已发生过这种故障。
漏项与重复,先挡在聚合入口
旧批次中 C 的成功记录不会填补本批次 C 的缺失;旧 execution 键同样排除。函数把它们计入 ignored_reports,原清单中的 C 继续出现在 missing_reports,execution_ended 为假。这一保守覆盖结果不是要改写云端 job 状态;它表示应用尚未完整覆盖此次授权目标,即使外部另一个字段显示完成,也不能对用户宣布全体受控设备已核实。
当前批次同一设备重复出现,直接拒绝输入,不用最后一条覆盖前一条。输入合同是“一次完整读取后,每个目标至多一条当前执行快照”,不是原始事件流。若采集层拿到分页重叠、重复消息或多个版本,应先按实际查询契约整理并保存原记录,再调用本函数;有冲突而无可信顺序时保留待核实,不能为了凑齐 A、B、C 擅自选一条成功。
设备 D 的记录则排除在原清单外,固定分母没有扩大。用户后来要求把 D 也关掉,应建立新的目标授权;组成员增删不应改写历史批次。例子没有实现分页或重命名解析,但缺 C、重复 A、额外 D 和旧键这几组输入,已经能让本地聚合暴露常见的覆盖错误。实际接入仍须验证全页读取和稳定设备身份映射。
旧时间 89、未来时间 101 和非可信来源 device_claim 三种观察都让 C 保持 UNKNOWN。trusted_sensor 只是输入合同中的标签,函数没有认证传感器,也不会替数据来源背书。真实系统要在采集层确认来源可信、单位正确、观察时间有意义;否则把普通设备自报贴上这个标签,就会再次把“宣称成功”伪装成独立物理证据。
本例接收字段完整、已经归一化的快照;不解析 MQTT、不处理原始 JSON 或事件乱序,不实现分布式事务和批次取消。它验证固定目标覆盖、证据分层、汇总与恢复候选的有限规则。下一步真机测试应保存目标、原始回报、可信观察及时间,再比较实际反馈和动作路径;本地 stdout 只能证明这组构造输入跑出了这些分支,不能作为三盏真灯已关闭的验收。
恢复计划是一组新的逐设备决定
若 B 可以恢复,新的执行与旧执行建立关联,并保留恢复原因、授权和目标。如果此时用户改口不再要求关闭,就停止尚未提交的恢复,已提交的动作按真实取消能力处理。恢复计划不是后台永远重复的队列,它依赖当前有效意图和设备证据。
即便关闭属于目标状态设置,重跑整批也会增加网络和设备负担,并可能覆盖后续意图。对于相对动作或一次性动作,风险更直接:已成功设备可能被再次触发。所以恢复集合来自逐设备判断,不是简单选所有 FAILED 或所有非 SUCCEEDED。状态名提供线索,实际是否可重做仍需动作语义与设备终态。
验收要覆盖遗漏,而不只覆盖失败
最后模拟晚到回报、恢复执行和用户新意图交错到达。旧批次的迟到成功应更新它对应的历史记录;新的设备状态按新鲜度合并;用户当前要求决定是否继续恢复。若最终只保留最后一条消息,系统很容易把旧结果当成新动作的证明。逐设备时间线能暴露这种错配。
测试产物至少包含固定目标、每台原始状态、观察来源与时间、聚合文案和恢复集合。模拟中的物理状态只是测试输入,不能作为真实设备成功证明。真机接入后再核实际动作和报告可信度,将两类证据明确分开。
给用户一份能继续处理的结果
批量结果首先要对应用户可识别的设备。可以先说“三台执行已收尾”,再逐项说明“门口灯已核实关闭,投影旁灯执行失败,窗边灯暂未确认”。设备名字来自下发时的对应关系,详细接口键留在后台。用户知道具体位置后才能检查或决定是否补做;只有数字的“成功一台、失败一台”不够完成交付。
如果设备很多,不需要把全部正常项逐条念完,但不能隐藏异常名单和未知数量。可以汇总已确认达成数量,点出需要用户处理的设备,并提供完整记录。对于语音交互,先讲主要结果,再回答追问;后台仍保留所有目标与证据,简短表达不等于简化判定。
还要说明结果观察时间。状态回报已经很旧时,可说某台上次报告关闭、当前待确认;实时查询成功时,说当前设备报告关闭。两种反馈都准确,只是保证不同。若产品对时间有严格要求,按目标使用合适的观察来源,不把周期旧值升级成实时物理事实。
恢复完成后,原批次与恢复批次分别保留。用户可以得到“原来一台失败,后来对该台补做已确认”的结论;不能把原记录改成三台一开始都成功。历史事实帮助之后定位故障频率,也能解释为什么某台收到多次指令。新恢复失败则同样点名未完成项,不以批次结束掩盖它。
等待边界由产品确定,不能为了得
本文由 AI图鉴自动抓取并翻译,版权归原作者所有;如需查看原文请点下方链接。
Batch task shows COMPLETED, why fail?
User to voice, Agent said, "Close all three lights in the conference room." Backstage batch shows COMPLETED, Agent answering "All are closed," but there's another light on the scene. This is a case of teaching and there are no real lights or AWS accounts. The engineering problems it exposed were the end of the batch, the success of the equipment reported, and the physical condition verified, three different things.
The AWS Iot Jobs life cycle document gives a clear reverse example: for snapshot job, the whole job can be converted to COMPLETED when all job applications reach the final state; these can include FAILED, TIMED OUT, REMOVED and CANCELED, not all SUCCEDED. Therefore, read as a whole COMPLETED, it can only say “this implementation has been completed” and not “every device has succeeded”. It's about AWS Iot Jobs, not another set of AWS IoT Companies API. Jobs documents include TIMED OUT of Execution as final; the rules that Commands cloud timeout can be overwhelmed with late returns cannot be applied. The document is again verified on October 13, 2026, and the overall closing conclusion of the body is limited to snapshot job.
First we drill from batch to each device.
AWS separates a job from each target device. The ListJobExercisesForJob in the CLI document lists the devices and their status; when you need to locate a device, find details of them using DiscribeJobExection. JobProcessDetails API also gives a breakdown of success, failure, timeout, cancellation, etc. Counts are suitable for the detection of anomalies, and equipment-by-equipment records are used to answer “which one needs to be processed”. list.
It is assumed that this list of targets will be fixed to A, B, C three lamps before it is released; eventually, A will report SUCCEEED, B will report FAILED, C will be recorded as TEMED OUT. Three applications can enter the final state, and the whole job thus shows COMPLETED. Agent's first reply should read: “The assignment has been completed; A has been successful, B has failed, C timeout. The physical status of the three lamps remains to be verified.” A/B/C is a teaching exercise, not a platform running record.
SUDCEEDED also has an evidentiary boundary. The AWS device workflow document describes the successful UpdateJobExecution report when the device is operational. The platform receives the performance status reported by the equipment; if the operation is to confirm that the lights are actually out, it will also require a credible equipment status or sensory return. Without such evidence, it is not possible to expand A's SUCCEEED to "the lights are off."
Counting leads to problems, so the equipment list can be located.
The platform envisages one successful, one failed, one timeout, three in total. This calculation indicates an anomaly, but it is not possible to tell the user which room to go to or which light to follow. Counts must be aligned with the fixed target list and equipment-by-equipment list, confirming that A, B, C have all the results, without repeating identity or missing items. Counting is just a mechanical check, not an action acceptance check.
If you have a page break in the list, the second page may contain the failed device. Reading the first page announces full success, not just omitting information, but changing the set of targets. The search process saves the integrity of the search boundary, returns the search boundary per page, and eventually overwhelms the device collection; the missing items are clearly listed as uncharted and cannot be considered as not abnormal in the event of a page break or permission restriction. There is no provision for the API page break to be realized, except for the current interface.
The summary and equipment-by-equipment recording of the platform may not be a snapshot at the same time. A change in the return on an item of equipment during the query is temporarily inconsistent with the list, with each query time and original result retained and reviewed according to the actual capability of the interface. Do not replace a state to bring the tables together. The application results should indicate the time of observation and not imply that all equipment is confirmed in the same physical moment.
Target equipment itself needs to be identified. When a user says that a conference room lamp displays a similar or later change of name on a device, the query key should not be matched only by a name string. The specific device identity and user-readable name are determined at the time of issuance, and the result is sent back to identity-related and then to the user-identifiable description. Neither the addition nor the removal of members in the equipment group can quietly rephrase the target for this batch.
If a target does not correspond at all to an example, the type of fact “do not create execution” should be retained. It may arise from failure to submit, permission, inability to match or incomplete queries, and cannot be directly confused with failure to execute equipment. A check of the submission stage and the query coverage is required before next step ' s additional submission or additional evidence can be determined. The end of the batch does not automatically make these omissions disappear.
Dismantling a user's intent into a device.
The application side saves a device-by-device record: the watch id, the target device set at the time of launch, each exection query key, the desired action, the deadline, the last platform report, and the last credible physical state. This is the design of the system proposed by the author, not the account book that AWS Jobs automatically maintains for Agent. The target set must be determined at the time of launch; the dynamic device set may be re-examined afterwards, the newly added equipment may be accounted for or the target may be omitted.
Convergence is calculated on the basis of evidence of the user's target. All three had credible results to prove that the lights had been turned off before they said “all were completed”; A had been verified as closed; B had been verified as not closed before “partially completed”; and equipment that could not be verified had been named as unknown. An operational objective, even if it had to be “one out of three”, could not, if it failed, erase a fact that had already been reported or verified, otherwise the resumption of the process would again be effective.
Business results do not equal the translation of platform status
Each result can be divided into several columns: what is the target, what is recently reported on the platform, what is a credible observation, whether the target is currently met and whether the old implementation is possible. The platform ' s status remains as it is, and business judgements are generated from these columns. Such a device, which is reported to have failed but which has now turned off the lights, can report that the current satisfaction of the target and the failure of its implementation still require diagnosis rather than forced success.
Similarly, a successful report continues to report the light, keeping the conflict and checking the source. Success may describe the completion of the software process, the state may be from old sampling, or there may indeed be equipment failure. No field can be covered by another field until the link and fresh evidence have been added. This is an example of a test design that does not claim that such a problem must exist for any manufacturer to achieve.
When you come together, ask if the user target requires that all be achieved. The "three shut-downs" are only achieved if all equipment is in its current state; if the target is to " turn off as many connected lights as possible ", the product may allow partial completion, but still requires an indication of equipment that cannot be identified. The criteria for success should be determined before issuance and cannot be changed to “most success is complete” on a temporary basis after failure has been observed.
The unknown also has different sources. The absence of queries, the failure of queries, the equipment timeout, the old state of operation and the conflict of implementation and status can all result in a temporary non-confirmation, but not in next step. User feedback in the first sentence is not required to cover all internal categories and can be named for equipment that remains unrecognized; the back-office retains specific reasons before the search, waiting, diagnosis or manual observation is selected for recovery.
It also makes a distinction between historical batches and the current state. The batch was closed yesterday, and today the lights may be turned on manually; the success of history cannot prove that it is still closed. The user asks “Is it all closed now” and should regain current observation rather than repeat history COMPLETED. If this objective is to be achieved only at a certain point in time, it should also indicate the time when the confirmation occurred and avoid a tacit commitment to continuity.
Restor with the equipment, do not run the whole batch.
B Failure of reporting does not mean that the whole batch can be re-executed. A The light may have been turned off and re-issuing would result in repeated actions; C Z timeout cannot prove that the equipment was previously non-activated, nor can the physical state be determined accordingly. Checks the specifics of B, C and credible equipment; only when it is confirmed that the old execution will not be followed and that the device will not meet its target will it decide whether to create a new execution in conjunction with the action risk. For devices in an unknown state, report unknown and suspend automatic replenishment.
If the business requires that “all or none of them” be turned off, and it has been verified that A off, B cannot turn off the lights, and that it wants to turn A on to a new device action, it may fail; it is not an automatic rollback of database services. The lamps may be selected by the user to accept partial results or manual processing; the high-risk equipment, such as door-barreling, electrical appliances, should stop auto-relaying and automatic compensation before the results are verified.
Duplicate run: " End" and "achievement goal" respectively
The aforementioned aggregation rules are presented below as operational examples in a standard library. Enter the three device snapshots that are constructed, not the AWS API response; the target is A, B, C all off. The fixed device at the time of release is placed in TARGETS, the function does not re-examine the dynamic device group, and the new device D does not enter the denominator of the original target. Here's the execution string that teaches the Query Link key without claiming that AWS Jobs has a generic field with the same name.
Reports keep the platform's executive records, and assigns separately the simulated physical observations. Observations can be used to determine whether lights have been turned off only if batches, performance keys, reliable sources of statements and time windows are consistent with this agreement. Time integer calculation: Currently 100, with 90 to 100 observations; this ten-unit window is only a business input condition in the example, not a threshold recommended by the manufacturer, and no real device clock sync.
Saves as watch result.py, running with Python 3.8 or more. Standard library only, no AWS SDK, no account configuration. Self-check relies on asert, not python-O.
python3 batch_result.py
"""Fixed three-device snapshot demo; constructed data, no AWS or device calls."""
from copy import deepcopy
TERMINAL = {"SUCCEEDED", "FAILED", "TIMED_OUT", "REJECTED", "REMOVED", "CANCELED"}
TARGETS = {"A": "exec-A", "B": "exec-B", "C": "exec-C"}
def summarize(batch, targets, reports, observations, now=100):
if not batch or not targets:
raise ValueError("empty batch or targets")
accepted = {}
ignored = 0
for row in reports:
device = row["device"]
if (row["batch"] != batch or device not in targets
or row["execution"] != targets[device]):
ignored += 1
continue
if device in accepted:
raise ValueError("duplicate current-device report")
if row["status"] not in TERMINAL | {"QUEUED", "IN_PROGRESS"}:
raise ValueError("unknown execution status")
accepted[device] = row
met, unmet, unknown, retry, diagnose = [], [], [], [], []
for device, execution in targets.items():
report = accepted.get(device)
obs = observations.get(device)
valid = (obs is not None and obs["batch"] == batch
and obs["execution"] == execution
and obs["source"] == "trusted_sensor"
and now - 10 <= obs["time"] <= now
and type(obs["off"]) is bool)
if not valid:
unknown.append(device)
elif obs["off"]:
met.append(device)
else:
unmet.append(device)
if report and report["status"] == "SUCCEEDED":
diagnose.append(device)
elif (report and report["status"] in TERMINAL
and report.get("old_stopped") is True):
retry.append(device)
goal = ("UNKNOWN" if unknown else "ALL_CONFIRMED" if len(met) == len(targets)
else "PARTIAL" if met else "NONE_CONFIRMED")
ended = (len(accepted) == len(targets)
and all(r["status"] in TERMINAL for r in accepted.values()))
return {"execution_ended": ended, "goal": goal, "met": met, "unmet": unmet,
"unknown": unknown, "retry_candidates": retry, "diagnose": diagnose,
"reports": {d: r["status"] for d, r in accepted.items()},
"missing_reports": [d for d in targets if d not in accepted],
"ignored_reports": ignored}
def report(device, status):
return {"batch": "batch-1", "device": device, "execution": TARGETS[device],
"status": status, "old_stopped": True}
def observation(device, off):
return {"batch": "batch-1", "execution": TARGETS[device],
"source": "trusted_sensor", "time": 100, "off": off}
def main():
rows = [report("A", "SUCCEEDED"), report("B", "FAILED"), report("C", "TIMED_OUT")]
obs = {"A": observation("A", True), "B": observation("B", False)}
result = summarize("batch-1", TARGETS, rows, obs)
assert result["execution_ended"] and result["goal"] == "UNKNOWN"
assert result["retry_candidates"] == ["B"] and result["unknown"] == ["C"]
print("terminal batch: goal=UNKNOWN retry_candidates=['B'] query=['C']")
all_off = {d: observation(d, True) for d in TARGETS}
result = summarize("batch-1", TARGETS, rows, all_off)
assert result["goal"] == "ALL_CONFIRMED" and result["reports"]["B"] == "FAILED"
assert result["retry_candidates"] == []
print("current goal met: ALL_CONFIRMED B_report=FAILED retry_candidates=[]")
mixed = deepcopy(all_off)
mixed["B"]["off"] = False
result = summarize("batch-1", TARGETS, rows, mixed)
assert result["goal"] == "PARTIAL" and result["retry_candidates"] == ["B"]
print("partial observation: met=['A', 'C'] unmet=['B']")
blocked = deepcopy(rows)
blocked[1]["old_stopped"] = False
assert summarize("batch-1", TARGETS, blocked, mixed)["retry_candidates"] == []
print("old execution not stopped: retry_candidates=[]")
success = [report(d, "SUCCEEDED") for d in TARGETS]
assert summarize("batch-1", TARGETS, success, obs)["goal"] == "UNKNOWN"
print("all reported success/missing C observation: UNKNOWN")
wrong = report("C", "SUCCEEDED")
wrong["batch"] = "batch-old"
missing = summarize("batch-1", TARGETS, rows[:2] + [wrong], obs)
assert missing["missing_reports"] == ["C"] and not missing["execution_ended"]
print("old batch excluded: missing_reports=['C'] execution_ended=False")
wrong["batch"] = "batch-1"
wrong["execution"] = "exec-C-old"
assert summarize("batch-1", TARGETS, rows[:2] + [wrong], obs)["missing_reports"] == ["C"]
extra = dict(report("A", "SUCCEEDED"), device="D", execution="exec-D")
assert summarize("batch-1", TARGETS, rows + [extra], obs)["ignored_reports"] == 1
print("wrong execution and extra D: excluded from fixed targets")
try:
summarize("batch-1", TARGETS, rows + [rows[0]], obs)
except ValueError:
pass
else:
raise AssertionError("duplicate report accepted")
print("duplicate current-device report: REJECTED")
stale = deepcopy(all_off)
stale["C"]["time"] = 89
assert summarize("batch-1", TARGETS, rows, stale)["unknown"] == ["C"]
stale["C"]["time"] = 101
assert summarize("batch-1", TARGETS, rows, stale)["unknown"] == ["C"]
stale["C"]["time"] = 100
stale["C"]["source"] = "device_claim"
assert summarize("batch-1", TARGETS, rows, stale)["unknown"] == ["C"]
print("stale/future/untrusted observation: C=UNKNOWN")
conflict = summarize("batch-1", TARGETS, success, mixed)
assert conflict["diagnose"] == ["B"] and conflict["retry_candidates"] == []
print("SUCCEEDED but observed on: diagnose=['B'] retry_candidates=[]")
print("PASS: fixed-target and evidence assertions")
if __name__ == "__main__":
main()
October 13th, 2026, Japan actually runs, exit code 0, standard output is:
terminal batch: goal=UNKNOWN retry_candidates=['B'] query=['C']
current goal met: ALL_CONFIRMED B_report=FAILED retry_candidates=[]
partial observation: met=['A', 'C'] unmet=['B']
old execution not stopped: retry_candidates=[]
all reported success/missing C observation: UNKNOWN
old batch excluded: missing_reports=['C'] execution_ended=False
wrong execution and extra D: excluded from fixed targets
duplicate current-device report: REJECTED
stale/future/untrusted observation: C=UNKNOWN
SUCCEEDED but observed on: diagnose=['B'] retry_candidates=[]
PASS: fixed-target and evidence assertions
The first line places three finalities in the same series: A Success, B Failure, C Z timeout, evaluation ended; however, A's observations are closed, B's still open, C has no physical observations, so the business result is UNKNOWN. It is not “complete” when most successes are successful, nor does it erase the unknown when there are failures. B goes to the restoration candidate, C goes to the query pool, and they move differently.
In the second line, all three of the currently closed analogue observations are added, with the result being all CONFIRMED; the original FAILED of B is retained and the candidate is returned to empty. This means that the current shut-down target has been met, that the historical implementation of B still fails and does not change the performance status of B to a success. The platform ' s failure may require diagnostics, but there is no need to start again on equipment that has reached its target in order to make history field green. In turn, these tectonic observations do not demonstrate which implementation process the equipment has completed, but only determine the current target status of the input.
In the third line, replace the observation of B with still open, and A and C remain closed; the target result is PARTIAL, with only B not reached. Compared to the first line, the change is only that C changes from missing evidence to verified; only after the unknown disappears can this group of inputs be described in its entirety as " partially achieved ". On this basis, the user is able to determine which of the remaining units needs to be processed, without having to guess whether part of the success is still hidden in offline equipment.
Resume the candidacy is not an automatic reorder.
The old stopped in the code is the independent tectonic evidence that the old execution will not start or continue any more. It is not an automatic sign after reading the final state, nor is it an AWS API guarantee. Turning B's field into a false one still knows that B is not active, but does not produce a restoration candidate; the code uses this inverse test to examine “unachievable targets” and “equipment that can now again function”.
Even if B appeared in retry candidates, no request was sent. The actual recovery will also examine the current user intent, action risk, validity and equipment protocol capability before deciding whether to establish a new implementation. Yes, C, physical results are not available first; yes, A, the target is met and the original result is retained. The polymer function delivers a well-founded pool of candidates that cannot be used directly to drive automatic retrys for all devices.
When the platform reports SUCCEEED and credible simulation observations are still open, this example places B in diagnose and not in the restoration candidate. While it is already visible that the current targets have not been achieved, it remains unclear why the conflict between implementation and observation has occurred. It may be necessary to check to see the connection to execution, the description of the equipment, or whether there is any follow-up; it would be easier to check to retain two sets of original evidence than to select one field to cover another column. This display shows the application processing of input conflicts without alleging that AWS has had such a breakdown.
Leaks and repeats.
The successful record of C in the old batch will not fill the missing C in this batch; the old exit key is also excluded. function. This conservative coverage is not intended to rewrite the cloud job state; it indicates that the application has not yet fully covered the authorized target and that even if another external field shows completion, the user cannot be declared to have verified the entire controlled device.
The current batch repeats the same device and directly refuses to enter without the last one covering the previous one. The input contract is a " current execution snapshot of each target after a full reading " and is not an original event stream. If the collection layer is given a page-splitting, duplicated message or multiple versions, the original record should be organized and kept in accordance with the actual search contract and then the function should be called; if there is a conflict without a credible order, it should be retained for verification, and a successful one cannot be chosen for the sake of consolidation A, B, C.
The records of Device D are excluded from the original list and the fixed denominator is not expanded. Users later requested that D also be shut down and a new target authorization should be created; group members should not be added or deleted to rewrite historical batches. Examples do not achieve page breaks or rename resolution, but the absence of C, repeat A, extra D and old key input already allows local aggregation to expose common overlay errors. Physical access continues to require full page reading and stabilization of equipment identity maps.
The old 89, the future 101 and untrustworthy sources allow C to keep UNKNOWN. Trusted sensor only enters the label in the contract, the function does not authenticate the sensor and does not endorse the data source. The real system needs to confirm that the source is credible, well-defined, and that the time of observation is meaningful; otherwise, the labeling of ordinary equipment will again disguise “claim success” as independent physical evidence.
field complete and consolidated snapshots are received in this case; distributional services and batch cancellations are not achieved without the analysis of MQTT, the processing of original JSON or events. It validates the limited rules of fixed target coverage, hierarchy of evidence, aggregation and restoration of candidates. The next step real machine test should save the target, original return, credible observation and time, and then compare the actual feedback and movement path; the local stdout can only prove that the structure entered out of these branches and could not be the acceptance check that has been shut down as three real lights.
The recovery plan is a new equipment-by-equipment set.
If B can be restored, new implementation is linked to old implementation and the causes, mandates and objectives of recovery are preserved. If, at this point in time, the user changes not to request closure, then the recovery that has not yet been submitted is stopped and the actions submitted are processed on the basis of real cancellation capacity. The recovery plan is not a line that will always be repeated backstage, and it relies on current evidence of valid intent and equipment.
Even if the closure were a target status setting, reruning the entire batch would increase the network and equipment burden and potentially cover subsequent intent. For relative or one-time actions, the risk is more direct: successful equipment may be triggered again. So the restoration is based on a device-by-equipment judgement, not just all FAILED or all non-SUCCEEED. The status name provides clues as to whether the actual redo is still action semantic and device-specific.
acceptance check to cover omissions, not just failures
The final simulation is late to return, resume execution and staggered arrival of the user ' s new intent. The successful late arrival of the old batch should update the historical record of its response; consolidate the new equipment in a fresh state; and the current user request to decide whether to continue the recovery. If only the last message is eventually kept, it is easy for the system to see the old results as proof of new action. The equipment-by-equipment time line reveals this mismatch.
The test products contain at least fixed targets, each primary state, observation sources and time, polymeric and recovery collections. The physical state of the simulation is only a test input and cannot be proved successfully as a real device. The actual operation and the credibility of the report after a real machine is accessed clearly separates the two types of evidence.
Give the user a copy of the results that will continue to be processed
Batch results are first matched to user-identifiable devices. It is possible to say “three implementations have been completed” and then item by item, “The doorlights have been verified as closed, the sidelights of the projection have failed and the window lights have not been confirmed”. The device name is derived from the corresponding relationship at the time of dispatch and the detailed interface key is left in the background. Only “one successful, one failed” in numbers is insufficient to complete delivery.
If there is a lot of equipment, it is not necessary to read all normal items by article, but it is not possible to hide the unusual list and the unknown amount. The quantities identified could be aggregated, equipment required for user processing could be identified and complete records provided. In the case of voice interaction, the main result is given before the question is answered; all objectives and evidence are retained in the background, and a brief expression does not amount to a simplified determination.
The timing of the results is also indicated. When the return on status is very old, it can be said that a given station was closed in the previous report and is currently awaiting confirmation; when real-time queries are successful, the current equipment report is closed. Both feedbacks are accurate, but they are different. When products are strictly time-bound, the use of appropriate observation sources is targeted and the old values of the cycle are not upgraded to real-time physical facts.
Upon resumption, the original instalment was retained separately from the resumed instalment. Users can obtain the conclusion that “the original one failed and later confirmed”; the original records cannot be changed to three records that were successful at the beginning. Once historical facts have helped to locate the frequency of failure, it can also explain why a certain station has received multiple instructions. A new recovery failed with the same roll-call as the unfinished item and did not cover it with batches.
Waiting for the boundary to be determined by the product.
Auto-fetched and translated by AI Atlas. All rights belong to the original author.