Align AXIS control flow and fix pause and program rendering

Document source UI gates, explicit native live-plot clearing, and remaining parity boundaries. Fix pause/resume transport continuity, repeated stepping, coordinate previews, and spindle override state. Optimize large program snapshots and rendering, and cover the control flows with native Tcl, WASM, and browser checks.

Validation: 22 native tests and source/browser checks pass. Worker/App regression: 113 of 123 pass; 10 pre-existing failures remain documented with no new failures.
This commit is contained in:
2026-09-08 10:46:27 +08:00
parent 845075f4a3
commit bade560372
55 changed files with 2495 additions and 286 deletions

View File

@@ -22,8 +22,9 @@ export class AxisOperationController {
try {
const response = await this.client.send(kind, payload, operationId);
const responsePayload = response?.payload && typeof response.payload === "object" ? response.payload : {};
const workerBlocked = response?.kind === "ERROR" || responsePayload.status === "blocked";
const workerBlocked = response?.kind === "ERROR" || ["blocked", "error"].includes(responsePayload.status);
this.trace.push(Object.freeze({ operation_id: operationId, kind, action, phase: workerBlocked ? "blocked" : "result", reason: responsePayload.gate_reason ?? responsePayload.message_key ?? responsePayload.code ?? response?.kind ?? null }));
if (workerBlocked) throw Object.assign(new Error(responsePayload.gate_reason ?? responsePayload.message_key ?? responsePayload.code ?? "axis_worker_rejected"), { response, operationId });
return Object.freeze({ operationId, response, gate: effectiveGate });
} catch (error) {
this.trace.push(Object.freeze({ operation_id: operationId, kind, action, phase: "error", reason: error?.message ?? String(error) }));

View File

@@ -14,4 +14,14 @@ assert.equal(jog.operationId, "axis-jog-start-000002");
const tick = await controller.execute("SIM_STEP", { cycles: 1 }, { state: ready, gate: false });
assert.equal(tick.operationId, "axis-sim-step-000003");
assert.deepEqual(controller.exportTrace().map((entry) => entry.phase), ["pending", "result", "pending", "result", "pending", "result"]);
for (const response of [
{ kind: "ERROR", payload: { gate_reason: "xyzac_machine_off" } },
{ kind: "COMMAND_RESULT", payload: { status: "blocked", code: "abi_missing" } },
{ kind: "COMMAND_RESULT", payload: { status: "error", message_key: "interpreter_error" } },
]) {
const rejected = new AxisOperationController({ send: async () => response });
await assert.rejects(rejected.execute("PROGRAM_RUN"), (error) => error.response === response);
assert.equal(rejected.exportTrace().some((entry) => entry.phase === "result"), false);
assert.equal(rejected.exportTrace()[1].phase, "blocked");
}
console.log(JSON.stringify({ schema: "axis-operation-controller-sample.v1", status: "verified", calls: calls.length, phases: controller.exportTrace().map((entry) => entry.phase), last_operation: tick.operationId }));