the Oracle Forms feasibility work in study/. The converter, the runtime, and seven forms converted end to end. Still builds, still deploys.
beta/docs/vocabulary.md
The words this project uses.
The words this project uses, so every conversation starts on shared language. An entry defines the word and points to the document that owns it. Facts, numbers and specs stay in the owning documents; the moment an entry carries a measurement, it is a second place for that fact to drift. When a new term gets coined in a design or a worklog, it gets an entry here in the same MR.
80 terms
The trees
5the research phase, another project's tree kept here so citations resolve. Read it and cite it, never edit it.
the product, a multi-tenant SaaS over the same converter. Design in beta/docs/.
the FMB XMLs under study/xml/ and everything measured from them: triggers, program units, items, LOVs. Sample forms until Green Waste's own arrive.
hello-flux's parked rules-engine design, the source of the value contract and the evaluator's guarantees. Design only, never implemented there.
The language
31the Clause Rule Language, in which all converted business logic is expressed and which is the only thing that runs. Owned by clause.md.
the unit of CRL, where the name comes from.
a complete CRL unit as stored in a FormSpec's rules, one trigger's worth of clauses. Always written with the prefix. Forms calls its own unit of PL/SQL a program unit, so a bare "program" is one word away from meaning the other thing.
one of the five things a clause can do: state, message, abort, navigate, report. The alphabet is closed, and closed is the point.
the effect that stops the operation, written deny at the top of a clause and as an abort inside a nested branch. Named for what RAISE FORM_TRIGGER_FAILURE does, which is to end the operation while displaying nothing.
when a program runs: on change, on save, on press, on load, and the rest of the collapsed Forms events.
the difference between change and save. A change rule is advisory: it shows a message, marks a field invalid, and blocks nothing. A save rule is blocking: it refuses the write and rolls the transaction back. CRL must never blur them.
every CRL program finishes, by construction. What buys static verification, transaction safety, browser execution and a checkable translation target. The type-theory name is total, kept in clause.md where it is defined and avoided elsewhere: this is a ledger product, and a running total is a different thing entirely.
one of the six set-based operations CRL has instead of a loop keyword. Owned by crl-loops.md.
the JSON AST, which is what is stored, executed and verified. The text syntax is a projection of it, deferred until the rule editor needs it.
the one key order for the whole language, in crl/golden/README.md. One table rather than one order per node type, so a writer needs no type dispatch and two writers cannot disagree about a node they classify differently.
the property that the schema accepts exactly what the serializer emits, so parsing a document and serializing it again returns the same bytes. It is why a schema lenient where the serializer normalizes is a defect and not a convenience.
what the zod schema does to the frozen encoding rather than replace it. A rule the encoding already stated is re-expressed in a second form, never re-decided.
the typed values every program is evaluated under: Decimal, split date and dateTime, strings on the wire. Owned by september/04-value-contract.md, adopted from EB-1.
the injected environment, clock and tenant timezone, so the evaluator never reads the system clock. Determinism as a guarantee, not a convention.
CRL's small set of pure functions and operators, taken from corpus usage.
the cloned decimal.js constructor all CRL arithmetic goes through, pinned to one precision and to Oracle's rounding. The global Decimal is never the one doing the arithmetic.
the one place a page string becomes a typed value, and therefore the one place blankness is decided. Nothing downstream trims.
every value travels as text. crlVersion is the only JSON number the language has.
a key the caller did not send, which means the rule could not be evaluated. Not the same as a blank one, which means the user left the field empty.
what a blocking point does with an absent operand, which is refuse. The alternative reads a check that never ran as a check that passed.
the outcome beside valid, refused and no verdict, carried as the evaluator's error. It always comes with an invalid verdict, because a value nothing could read must never let a blocking check through.
no Node API, no DOM and no framework, so the same code runs in a browser, on a server and in a test. A constraint on @valko/crl that a test enforces by reading the source, not a preference.
study/lib/ruleEngine.ts, now a re-export of the package. The one place the study adapts to a type the package keeps deliberately narrower than the study's own.
the package surface carries both the study engine's eight rule kinds and CRL's twenty clause kinds. Sprint 3 retires the older one; until then a consumer of either finds it in one place.
the effect that runs an Oracle report, always written in full. A bare "report" in this project is a document: the coverage report, a bug report.
CRL's one escape hatch, routing to a ported program unit or a captured package. A target that is neither becomes a gap.
the clause that writes, into the record under evaluation or into session state. Not one of the five effects, which act outside the record. What the study's populate and compute both became.
and, or or not, combining guards. Distinct from the eleven operators, which combine values. not is the negation of a guard being true, so it fires where PL/SQL's Else fired, on false and on null alike. Listed in clause.md.
Forms' own runtime values, Record_Status, Form_Status, Cursor_Record and Last_Record. A read-only namespace of its own, never a session global, and closed: a fifth name needs a corpus site.
an argument the form was called with, read through a parameter node and never written. A fifth namespace beside page fields, session globals, lookup columns and system state, and not the Forms parameter list, whose handles the platform supersedes. Listed in clause.md.
The converter
26a closed matcher that turns one Forms idiom into CRL. It never interprets mechanics and never guesses; an unmatched shape is refused.
the third outcome beside pass and fail, and correct behaviour, not failure. A refusal becomes a gap. If refusals score as failures, the pressure is to convert unsafely.
what did not convert, carried in the FormSpec with its source text and fingerprint. The customer-facing worklist, not an appendix.
the normalized hash of a refused shape, so one procedure copied seven times files one report, not seven.
emit a claimed effect only when every enclosing guard on its path is also claimed. The narrowing of the all-claimed gate that keeps its safety argument.
the study's original gate: one unclaimed node refuses the whole trigger. Correct about ancestors, stronger than needed about siblings.
what a recognizer does to a statement it understands, which is take responsibility for it. A claimed statement becomes a clause or is consumed; an unclaimed one becomes a gap. A recognizer that claims a statement and emits nothing for it is the one failure the coverage rule exists to catch.
a claim that produces no clause, because the statement is half an idiom whose other half carries the meaning, or an explicit do-nothing. A NULL; is consumed and a SELECT COUNT(*) feeding an exists is consumed.
how a nested raise is stored. There is no if clause; the enclosing guards are conjoined into the clause's own when, so the gate's rule is a shape in the document rather than a step in the converter.
every non-blank statement of a trigger's body is accounted for by a gap that quotes it, a clause that reads it, a value help on the field, or being a bare NULL;. Nothing may be none of the four.
a statement that is none of the four. Work the converter neither did nor put on the worklist, and the one failure a coverage number cannot show, because a dropped statement makes the converted fraction look better rather than worse.
the data source whose row a program's row-reading clauses read, named once on the program in lookup rather than per clause, because one Forms trigger reads one row. Id-prefixed check-, against a value help's lov- and a block's records-.
an owner carrying more than one Forms event that collapses to the same trigger point, which 82 of the corpus's 529 programs are in. Both members name their event in the id, so which came first in the document decides nothing.
a control whose whole job is to open a field's value list, the small torch Forms put beside a field. Read from its trigger body and never from its name, and recorded as forms.opensValueList on the button's own field, so it converts to neither a program nor a gap.
the hand-translated CRL programs, the 29 verified plus the stress set, that the evaluator and the converter are tested against. Lives in beta/crl/, and crl/golden/README.md owns its JSON encoding.
one member of that set: one file, one trigger point, its clauses in evaluation order, the PL/SQL it was read from, and the cases it is expected to satisfy.
the two halves of the golden set. A verified translation is read from an existing verified test and names it. A stress translation is written against a corpus body picked for what the verified half never exercises, and has no test behind it, which is why it names none.
the golden set's own check, beta/crl/golden/golden.test.mjs. It holds every golden file to the encoding and carries a second implementation of the serialization rules on purpose, because byte-for-byte determinism rests on two writers agreeing.
the one door anything asks what a CRL program does through, in beta/crl/src/harness/. Four doors rather than one, because the trigger point decides how an absent operand reads. Never a fixture: a fixture lets a verification pass while the thing it stands for refuses the user's own choice.
the programs a harness looks up in, supplied by its caller. The golden set off disk this month and a FormSpec's rules in sprint 3, with no change to the interface between them.
of an expect case, that the evaluator cannot reach its stated outcome because the encoding carries no input for something the program reads. A case is marked with its reason, run anyway, counted, and fails the golden runner the day it becomes drivable.
how the golden runner reads outcome.values. A case may name fewer fields than the program wrote, so the fields it names must match and the rest are not asserted.
a rule counts as verified only when a test reads its expectation from the source PL/SQL quoted in its own docstring. The discipline the whole metric rests on.
classified as converting to nothing because the platform already does the job, like Forms' master-detail plumbing and the form lifecycle calls. Not a gap, not a failure.
a database package's source taken through /api/db/query, or not yet. An unresolved name is usually an uncaptured package, not a parser bug.
same XML in, same output out, byte for byte, with the converter's clock injected. Asserted in CI.
The document
5one converted form as one document: sources, blocks, fields, rules, gaps. Owned by formspec.md.
what a field is, not which widget drew it in 1998, carried as role on the field. The renderer decides the component; Oracle specifics stay in the forms block that nothing else reads. Never "control", which Forms already spends on a control block, a block with no table behind it.
a query-backed picker on a field. A pick is a patch to the record, not a value returned to one input.
a declared, named query in the FormSpec. Components call it by id and never compose SQL.
the map from role to component the renderer resolves through. Its widened context is the second contract formspec.md owns.
The product
6the SaaS reads and writes the customer's existing Oracle. The only account type the beta ships.
the PostgreSQL migration that ends the customer's Oracle licence fee. Designed for, not built. The reason logic must live in CRL and never in PL/SQL.
the mode whose changes alter the tenant's data model, uploads and the rule editor included. Distinct from admin-gating, which is permission, not mode.
where daily work happens over the model engineering mode defines.
where a human completes the worklist the converter could not. Layer 2, out of the September scope.
what remains for a human after conversion: the gaps, in editable form.
The process
7the per-item record of what was done, found, decided and left open, under beta/docs/september/. It never restates a spec.
the discipline this vocabulary also obeys. A fact stated twice drifts in both places.
one row of the September plan, the unit of status, and nothing else. Forms calls a field an item, so in beta/ the Forms construct is always a field. The count lives in formspec.md, which owns it.
the sequencing of items in september/sprints.md: a sprint holds phases, a phase holds tasks, and a task references its item.
the dated condition a sprint ends at, defined in the September README. Not a gate, which is a converter's safety filter, as in the path gate.
the checkpoint where the vocabulary stops moving, so the AST is designed against a settled language. After it, a shape the corpus needs is the converter's problem and a gap, not a word added to clause.md.
the full path from XML through FormSpec and CRL to an evaluated verdict. What the September deliverable proves end to end.
Four terms are load-bearing: advisory / blocking, refused, superseded and verified. Each carries a safety distinction rather than a shade of meaning, so using one loosely does not merely miscommunicate, it specifies a defect.
The markdown file is the record and this page is a reader. An entry defines the word and points to the document that owns it; facts and numbers stay in the documents that own them.