|
| 1 | +# Record runtime profile |
| 2 | + |
| 3 | +`record-runtime-v1` describes the behavior exercised by `v1/runtime.json`. |
| 4 | +It is an experimental test contract for native Rust and Go WASI, with no EQL |
| 5 | +resolver. Python and TypeScript runners remain pending. |
| 6 | + |
| 7 | +Each case uses one field, `value`, with ciphertext (`c`) and equality (`eq`) |
| 8 | +outputs, under context `["conformance", "value"]`. `kind` declares its type; |
| 9 | +`null` is empty for a required value, `sql`, or `sealed`. Values use the scalar |
| 10 | +profile's `{type, text}` representation, extended with `float64` decimal text. |
| 11 | +Runners construct typed values before calling the engine. This profile admits |
| 12 | +`int32`, `int64`, `uint64`, `float64`, and `string` as declared kinds; |
| 13 | +both runners reject unknown kinds and null-policy spellings. |
| 14 | + |
| 15 | +## Write and read |
| 16 | + |
| 17 | +The `seal` operation receives an ordered `rows` array of field maps. An absent |
| 18 | +map key is missing; `{ "type": "null", "text": "" }` is an explicit null. |
| 19 | +These are separate inputs, including in languages that offer a nil-like value. |
| 20 | + |
| 21 | +Successful cases specify `expected_rows` and `expected_stored`. The former |
| 22 | +describes opened values with their types and order. The latter states whether |
| 23 | +each row stores the field at all. Both must have the input row count. |
| 24 | + |
| 25 | +- Required fields reject missing values and explicit nulls. |
| 26 | +- SQL-null fields accept both, omit the stored field, and open as explicit null. |
| 27 | +- Sealed-null fields require the key to exist. An explicit null stores both |
| 28 | + ciphertext and equality, then opens as null. |
| 29 | +- Runtime writes require the declared numeric type. A losslessly convertible |
| 30 | + value of another width is still refused on this path. |
| 31 | +- Unknown fields are refused, including when an omitted SQL-null field makes |
| 32 | + the input field count match the declaration; they cannot be silently dropped. |
| 33 | +- An invalid later row rejects the whole batch before data-key generation. |
| 34 | +- An empty batch succeeds with no data-key calls. |
| 35 | + |
| 36 | +Every stored equality term must match a query for its expected opened value. |
| 37 | +`equal_lengths` additionally requires all stored ciphertexts in the case to |
| 38 | +have equal lengths. Mixed sealed-null/value cases exercise this requirement |
| 39 | +for int32, int64, uint64 and float64. This is not a claim about all types. |
| 40 | +Boolean equality is not admitted; existing native null tests cover boolean |
| 41 | +ciphertext separately. |
| 42 | + |
| 43 | +## Equality queries |
| 44 | + |
| 45 | +The `query` operation receives `input` and either `expected_value` or |
| 46 | +`expected_error`. A successful query's term must equal the term stored by |
| 47 | +encrypting `expected_value` through the same declaration. That value is |
| 48 | +independent expected data, not a conversion computed by the runner. |
| 49 | + |
| 50 | +Cases cover signed narrowing bounds, integral and fractional float inputs, |
| 51 | +negative-to-unsigned refusal, and exact versus rounded integer-to-float |
| 52 | +conversion around 2^53. Text and boolean values are not parsed as numbers. |
| 53 | +A null equality query succeeds only for sealed-null fields. |
| 54 | + |
| 55 | +## Refusals and observations |
| 56 | + |
| 57 | +`expected_error` specifies the engine diagnostic `code`, `reason`, and `field`. |
| 58 | +Native Rust must reject before producing a pending operation. Go must expose |
| 59 | +`ErrEncoding` and the same diagnostic when the request reaches the guest. |
| 60 | + |
| 61 | +Missing and unknown fields can be rejected earlier by Go's record adapter. |
| 62 | +The adapter preserves the same structured source diagnostic (code, reason and |
| 63 | +field); the corpus does not require a particular English message or rejection |
| 64 | +at the host boundary. Forwarding to the guest later remains conformant. |
| 65 | + |
| 66 | +Each refused source row contains exactly one independent fault. The corpus does |
| 67 | +not specify precedence between multiple faults or source-object traversal order. |
| 68 | +Batch cases may place that single invalid row after valid rows to test preflight. |
| 69 | + |
| 70 | +The native runner instruments data-key generation and retrieval calls. A |
| 71 | +successful batch makes one of each when it stores ciphertext, and zero when |
| 72 | +every field is omitted. Refused writes and all queries make neither call. |
| 73 | +The query oracle's separate encryption is counted separately. These assertions |
| 74 | +exclude index-key initialization and do not establish Go provider-call counts. |
| 75 | +Go runs the same semantic cases through its deterministic WASI guest; that |
| 76 | +guest's lack of network requests is not proof of logical key-call counts. |
| 77 | + |
| 78 | +## Scope and remaining work |
| 79 | + |
| 80 | +The cases cover runtime writes, corresponding reads, and equality queries. |
| 81 | +They do not cover malformed stored records, authenticated type mismatches on |
| 82 | +read, context composition/preflight, provider failures, or other operators. |
| 83 | +Fresh cross-language artifact exchange remains the scalar profile's coverage. |
| 84 | + |
| 85 | +Existing `value_kinds.json` and `query_operators.json` fixtures remain the |
| 86 | +shared evidence for their wider type/operator matrices. Existing generated |
| 87 | +Go null tests retain pointer mapping and EQL assertions. Native null tests |
| 88 | +retain their wider kinds and lower-level padding assertions. No tests are |
| 89 | +deleted by this slice; consolidation requires matching those assertions first. |
0 commit comments