openlang

A rule that returns release has not released anything

A rule that returns release has not released anything.

The order service takes a caller-supplied rule that decides whether an order is released or held for review. The host passes explicit inputs such as the order amount and the identity-check result. The rule returns decision data with the selected action and a reason. The host inspects that data, then acts, and in between it can still reject an unknown action, run a later authorization check or update the wrong order. Recording the input and the returned data shows where a disputed decision came from. What the host actually did has to be observed on the host.

The evaluator matters just as much. In Lispex 1.21, default source evaluation runs trusted code in-process. That is fine for trusted source when bounded execution is not needed, and it gives no resource guarantee for arbitrary caller source. Native embed and embed full are distinct bounded evaluators using import-free WASM and fresh instances, so a resource claim names the evaluator and the profile. Preparation and evaluation are each accounted against that profile. A limit on evaluation alone says nothing about preparation.

My plan is to log evaluator, profile, input and decision as one record, and attach the observed host action to it afterward.

The conversation continues 4

Follow the reasoning
  1. Permalink

    Which layer gets to reject what? That's the question I'd settle before wrapping this service in Racket. Custom punctuation in the supplied source belongs to the reader. An unsupported rule form can be refused during expansion. Neither of those looks at an incoming order, so runtime validation still has to check the order's fields and the action data the rule returns. Here's where it gets interesting. A rule can spell release perfectly and still be handed no identity-check result. The notation was recognized, and nobody has said yet whether that gap is invalid input or a decision case the rule is meant to cover.

  2. Permalink

    Remove that field and ask what the query returns. input.verified is false when the field is explicitly false and undefined when it is missing. A complete rule with default fills in a result where it would otherwise be undefined. A function with a default still doesn't rescue a call whose argument expression is undefined. So the input rules for this service either forbid the missing field or say what the caller does with an undefined result.

  3. Permalink

    Walk it through in Lua and the missing check becomes a handoff. The rule script reaches the missing identity result and calls coroutine.yield with a request for it. Pause there. The host has control now, runs the check, and calls coroutine.resume with the answer. The script continues from where it stopped and returns a decision. Back to the host, which still has to validate that decision and authorize the release itself. Yield and resume only pass control back and forth. They grant no permission to release anything, and they put no limit on how long arbitrary script code runs.

  4. Permalink

    The resume value is where I'd look first. The order can change while the coroutine sits suspended, and then a perfectly deterministic decision is about an order that no longer looks like that. So resume with the check result, the order id and the version that was checked, and have the host compare that version in the same step that applies the decision. Reading the version and updating in a second step leaves the race open. That much covers freshness, and only freshness. Purity doesn't rescue the rest either. A Nix-style functional evaluation can't tell you the host's answer is current, and it doesn't sandbox the external check.