# Batch 3 — Builtins & Types — Findings

**Date:** 2026-03-26
**Files reviewed:** `builtins/constantsRegistry.js`, `builtins/fieldRegistry.js`, `builtins/functions.js`, `builtins/helpers.js`, `builtins/tableSynonymsRegistry.js`, `builtins/functionsRegistry.js`, `types/acurityTypes.js`
**Status:** Complete

---

## Summary

The builtins layer is largely well-designed static data with clean accessor interfaces.
`fieldRegistry.js` and `tableSynonymsRegistry.js` are exemplary. The main finding is a
**structural inconsistency in `functionsRegistry.js`**: some function entries use
`{ type: 'UNKNOWN' }` objects for params instead of bare type strings, which is inconsistent
with how every other entry is structured and could cause validator runtime errors. A secondary
thread is the type-set fragmentation already identified in Batch 1: `AcurityTypes`, `ACURITY_TYPES`
in `constants.js`, and the inline type lists in `parser.js` remain three separate uncoordinated
sets. `helpers.js` has a minor `hasOwnProperty` safety issue. No critical issues; no architecture
violations.

---

## Findings

### `functionsRegistry.js`

#### [MAJOR] Inconsistent `params` array format — object entries mixed with bare type strings — lines ~1387, ~1515, ~1658

**Issue:** The `BuiltInFunctions` registry uses two incompatible formats for `params` entries:

**Format A** (standard — used by most entries):
```javascript
params: [AcurityTypes.STRING, AcurityTypes.SHORT]   // array of type strings
```

**Format B** (used by a handful of entries — e.g. `PAYHIST_CHGE_KEY`, `TMP_CONT_CRTR`, `SHCRTHST_CHGE_KEY`):
```javascript
params: [{ type: 'UNKNOWN' }]   // array of objects
```

Any validator that iterates `params` expecting type strings (the dominant format) will receive
a plain object `{ type: 'UNKNOWN' }` instead and either compare it incorrectly or throw.
`validateKnownFunctionArgumentCount` in `functionResolution.js` reads `signature.params.length`
which is safe, but any code that reads individual param values by index would break.

**Recommendation:** Normalise all entries to Format A. Replace `[{ type: 'UNKNOWN' }]` with
`[AcurityTypes.UNKNOWN]` (a bare string). This is a data-only change with no logic impact.

---

#### [MINOR] Mixed string literals and `AcurityTypes` constants for `returns` — throughout

**Issue:** Approximately half of the entries (wave-1/wave-2) use `AcurityTypes.SHORT` etc.
for `returns`; the wave-3 and database-section entries use plain string literals like `'SHORT'`,
`'DOUBLE'`, `'DATE'`. Both produce the same runtime values since `AcurityTypes.SHORT === "SHORT"`,
but the inconsistency makes the file harder to read and creates a latent risk if `AcurityTypes`
values ever need to change.

**Recommendation:** Normalise all `returns` values to `AcurityTypes.*` constants. This is a
data-only search-and-replace with no logic impact.

---

#### [OBSERVATION] `bindsHandle` metadata format is undocumented in the file header — line 27 (schema comment)

**Issue:** The schema comment block (lines 24–34) documents `params`, `paramModes`, `returns`,
`varArgs`, and `description` fields. The `bindsHandle` field (used by `handleBindingAnalysis.js`)
is not mentioned. A developer adding a new index-opening function wouldn't know to include it.

**Recommendation:** Add `bindsHandle` to the schema comment block:
```
 *  bindsHandle: { handleParam: number, tableCodeParam: number }
 *               -- present on functions that open a database handle (e.g. INDEX_OPEN_STANDARD).
 *               -- handleParam/tableCodeParam are the zero-based argument indices.
```

---

#### [OBSERVATION] Source data is v15; production runtime is v25

**Issue:** The file header documents this gap and uses `@corpus-inferred` annotations for the
41 functions discovered from real-world report analysis. This is good transparency. The
implication is that some v25 functions may still be absent from the registry, causing the
function validator to fire false "unknown function" diagnostics for valid v25 calls.

**Recommendation:** Accept the current approach. When v25 documentation becomes available,
run a gap analysis against the current registry and promote corpus-inferred entries to
formally documented ones.

---

### `helpers.js`

#### [MINOR] `isBuiltInConstant` uses potentially unsafe `hasOwnProperty` pattern — line 31

**Issue:**
```javascript
return BuiltInConstants.hasOwnProperty(upperName);
```
If `BuiltInConstants` were ever to contain a key named `"hasOwnProperty"` (e.g. if the runtime
added a constant with that name), this call would behave incorrectly — it would return the
function itself rather than `true`. The safer form used in `tableSynonymsRegistry.js` is:
```javascript
Object.prototype.hasOwnProperty.call(BuiltInConstants, upperName)
```

In practice, the static registry would never contain such a key. But the inconsistency across
the file is worth fixing for defence-in-depth.

**Recommendation:** Replace with `Object.prototype.hasOwnProperty.call(BuiltInConstants, upperName)`.

---

#### [OBSERVATION] `getFunctionSignature` confirmed case-normalised (resolves Batch 2 observation)

**Finding:** `getFunctionSignature` (line 19–22) normalises to `upperName` before lookup.
This resolves the Batch 2 observation that `functionResolution.js` called it with the original
case — all lookups are correctly case-insensitive. No action needed.

---

### `constantsRegistry.js`

#### [MINOR] `HEADER`, `BODY`, `FOOTER` defined as both keywords and built-in constants

**Issue:** `constants.js:ACURITY_KEYWORDS` includes `"HEADER"`, `"BODY"`, `"FOOTER"` as
language keywords (block-section markers). `constantsRegistry.js` also defines them as
built-in boolean constants (`AcurityTypes.BOOLEAN`) at lines 45–47.

In Architect, `HEADER`, `BODY`, and `FOOTER` are section delimiters *and* boolean flags
(`HEADER` is true during the header section). The dual role is real. However, validators
that check "is this identifier a keyword?" using `ACURITY_KEYWORDS` will reject them,
while validators that check "is this identifier a known constant?" will accept them. This
inconsistency could produce conflicting diagnostics.

**Recommendation:** Verify from the language spec whether `HEADER`/`BODY`/`FOOTER` should
be blocked from `extractVariables` and similar keyword-skip paths. If they are also valid
constant identifiers (readable in expressions), they should be removed from `ACURITY_KEYWORDS`
or given special-case handling.

---

### `tableSynonymsRegistry.js`

#### [OBSERVATION] 2-space indentation — inconsistent with codebase convention

**Issue:** The file uses 2-space indentation throughout, while all other reviewed files use
4-space indentation.

**Recommendation:** Cosmetic only. Normalise on next edit; not worth a dedicated fix.

---

### `fieldRegistry.js`

No logic findings. The file is large (3563 entries, ~318KB) but is entirely generated data
with two clean accessor functions. Both `isKnownFieldHandle` and `getFieldEntry` correctly
normalise to lowercase before lookup.

---

### `acurityTypes.js`

#### [OBSERVATION] `AcurityTypes.BIGINT` absent from `constants.js:ACURITY_TYPES` (cross-reference)

**Issue:** `AcurityTypes` (this file) defines `BIGINT`; `constants.js:ACURITY_TYPES` does not
include it. This is a continuation of the type-set fragmentation flagged as **Major** in Batch 1.
No new action needed here beyond what is already in the backlog.

---

#### [OBSERVATION] `inferTypeFromLiteral` integer range relies on prior regex guard — lines 157–163

**Issue:** `parseInt(trimmed, 10)` is called without a `NaN` check. However, this function is
only called after the caller has matched `/^-?\d+$/`, so `trimmed` is always a valid integer
string at this point. No real risk, but the function would silently mislabel `NaN` as `LONG`
if called in isolation.

**Recommendation:** Accept as-is. Document the precondition in JSDoc if the function is ever
made public API.

---

## Positive observations

- `fieldRegistry.js` uses `Object.prototype.hasOwnProperty.call` (correct) and normalises to
  lowercase in both accessors — exemplary pattern.
- `tableSynonymsRegistry.js:isValidTableSynonym` also uses the safe `hasOwnProperty.call`
  form — the correct pattern.
- `functions.js` barrel is a clean re-export with no logic — correct separation between
  registry data, helper logic, and external API surface.
- `functionsRegistry.js` wave-3 entries consistently use `varArgs: true` with `params: []` —
  an honest representation of "we know this function exists but not its exact signature."
- `acurityTypes.js:areTypesCompatible` correctly allows `UNKNOWN` to be compatible with
  anything — prevents false positives from incomplete type inference.

---

## Actions for backlog

| Severity | File | Issue | Recommended action |
|---|---|---|---|
| Major | `core/src/builtins/functionsRegistry.js:~1387` | `params: [{ type: 'UNKNOWN' }]` format in some entries | Normalise to `[AcurityTypes.UNKNOWN]` (data-only change) |
| Minor | `core/src/builtins/helpers.js:31` | `hasOwnProperty` called directly on object | Replace with `Object.prototype.hasOwnProperty.call(...)` |
| Minor | `core/src/builtins/functionsRegistry.js` | String literal `returns` values mixed with `AcurityTypes` constants | Normalise to `AcurityTypes.*` constants throughout |
| Minor | `core/src/builtins/constantsRegistry.js:45` | `HEADER`/`BODY`/`FOOTER` as both keywords and constants | Verify language spec; decide on canonical role |
