FS FormScript

Conformance

The declared level

Level 3, of the five levels in §15.1. The declaration lives in conformance.json at the root of the repository, and it is checked rather than asserted:

bash
npm run formscript -- level

The command re-reads §6.4 from the specification, compares its builtin list with the evaluator's, and reports every level against what the repository actually contains. A test fails if the declared level is not supported.

Level Requires Status
0 Lexer, parser, semantic analysis, error catalogue met — the catalogue defines 79 codes
1 Reactive engine, validation, output schema met — §18.5 drives the flagship to one output document
2 All builtins, i18n, a11y metadata generation met — all 67 builtins of §6.4
3 source, action, offline queue, pending states met — the runtime resolves, executes and queues
4 Multi-platform conformance to Spec-15.6 not claimed

Level 4 needs two independent runtimes that produce identical field values, validity states, validation error codes and output documents for a fixed document and a fixed script of transactions (Spec-15.6). This repository holds one runtime, so the level is not claimed. That is the honest position: a claim of level 4 that cannot be checked is worse than no claim.

The suite of §18

The specification's own conformance suite is executed, not asserted. All five sections run:

Section What it checks Rows Result
§18.1 Documents that must parse 46 pass
§18.2 Documents that must be rejected, with the expected code 24 pass
§18.3 Evaluation semantics — the value and the static type 26 pass
§18.4 Documents that must compile 4 pass
§18.5 The §17.2 flagship document, end to end 1 pass
bash
npm run conformance
# or
npm run formscript -- conformance

The rows are read from the specification text at run time, so a row added to §18.3 is picked up by the next run — there is no second copy to keep in step.

The two rows that are easy to get wrong

§18.3 distinguishes result type from result value. The row 1 + 1 → 2 and the row 1 + 1.0 → 2.0 have the same value and different types. The runner compares the value with strictEquals and the static type with the analyser's inferType. A runner that only checked the value would pass a runtime that computed integers where decimals were required.

§18.5 asserts the keys that must be absent. The flagship's output document omits an inapplicable entity, so the expected result names postcode, guarantor.guarantor_name and guarantor.guarantor_consent as absent (Spec-4.35). A runtime that emitted an inapplicable field as null would fail this row.

The gates

Everything the repository claims is a command that can be run:

Gate Command Result
The specification is self-consistent npm run verify:spec 0 errors, 0 warnings, 19 notes
The packages build npm run typecheck exit 0
The code lints npm run lint 0 findings
The tests pass npm test 776 passed over 21 files
The suite passes npm run conformance 101 passed, 0 failed
Decimal precision npx tsx scripts/check-decimal-precision.ts 5 of 5 checks
The level is supported npm run formscript -- level level 3 declared

npm run verify:all runs the first four in sequence.

The 19 remaining notes from the specification's own lint are all unused-error-code: a code that §14 defines and the prose does not yet mention. They are notes, not errors, and each one is a small piece of housekeeping the specification still owes its reader.

What the reference implementation does not yet do

The level is a claim about conformance, and the honest version of it says what is missing:

  • The engine does not execute action blocks, and does not fetch source blocks — those belong to §11 and to the runtime, which implements them. A field that reads a source sees null in the engine.
  • The optionsFrom option set is modelled in the dependency graph but not materialised.
  • state blocks are parsed but are not exposed to expressions.
  • The §18.2 runner checks that the expected code is the first error reported for the row (Spec-18.2), not merely one of the codes. It does not require the row to yield exactly one code: the options/optionsFrom row reports E-CONFLICT-06 and then E-REF-01, because the fragment also names a source it never declares.

These are recorded in the implementation's progress log, not hidden.