# Architect Language Tools — Supplementary Module

This package is both:

1. **The reference implementation** of the supplementary module contract for Architect
   Language Tools. Use it as a working template when authoring your own module.
2. **An end-to-end test fixture** — it is imported by the core integration test suite to
   verify that every injection point works correctly.

---

## What the supplementary module does

The core plugin ships with a base set of builtins (functions, global variables, tables,
help entries) and a fixed set of validators. An organisation that deploys the plugin
typically needs to extend these with:

- **Functions and global variables** specific to their runtime environment.
- **Table definitions** for their own database schema — index structures, field names,
  and types that the core plugin has no knowledge of.
- **Validators** that enforce organisation-specific coding standards.
- **Severity overrides** that tune which core diagnostics are errors vs. warnings within
  their environment.

The supplementary module is the mechanism for all of this. It is a single JS file (or a
folder with an `index.js`) that the core plugin loads at startup. The author writes the
module content; the core plugin owns the loading mechanism.

---

## Module contract

The module's default export must be a plain object conforming to this shape:

```js
{
  validators?:        SupplementaryValidator[],
  severityOverrides?: { [messageId: string]: "error"|"warning"|"information"|"hint"|"off" },
  builtins?: {
    functions?:       { [UPPER_NAME: string]: FunctionEntry },
    globalVariables?: { [UPPER_NAME: string]: GlobalVariableEntry },
    tables?:          { [TWO_CHAR_CODE: string]: TableRegistration },
    help?:            { [UPPER_TOKEN: string]: string }
  }
}
```

All top-level keys are optional. An empty object `{}` is valid and produces no effect.

The loader validates this shape at registration time and throws a descriptive error on any
violation — so mistakes are caught immediately rather than silently degraded.

---

## Extension points

### `validators`

An array of validator functions. Each runs against every Architect document in the
validation pipeline, after all core validators have completed.

```js
function myValidator(document, context) {
    // document      — VS Code TextDocument or PlainTextDocument
    //                 Has: .lineCount, .lineAt(i).text, .getText(), .fileName, .uri.fsPath
    // context.facts     — full documentFacts object (AST, symbol table, scopes, etc.)
    // context.filePath  — absolute path string (convenience alias for document.uri.fsPath)
    // context.config    — .architect.json config object, or null
    return [ /* diagnostic[] */ ];
}
```

Each returned diagnostic must be a plain object:

```js
{
    range: {
        start: { line: number, character: number },  // 0-based
        end:   { line: number, character: number }
    },
    message:  string,
    severity: 0 | 1 | 2 | 3,   // Error=0, Warning=1, Information=2, Hint=3
    code:     string,            // your rule ID — must NOT start with 'ACU-'
    source:   string             // recommended: 'architect-ext'
}
```

Supplementary validators are additive. They cannot suppress or override core diagnostics.

Rule IDs must use a namespace that does not collide with core IDs (`ACU-XXX-NNN`). The
recommended convention is an organisation-specific prefix agreed at deployment time (e.g.
`EMP-STY-001`).

---

### `severityOverrides`

Maps core diagnostic message IDs (`ACU-XXX-NNN`) to a severity string. Positions at
**layer 2** in the resolution chain — above catalog defaults but below all project,
workspace, file, and inline tailoring.

```js
severityOverrides: {
    "ACU-INT-002":  "warning",   // downgrade from error to warning organisation-wide
    "ACU-TODO-001": "off"        // suppress entirely at organisation level
}
```

Valid values: `"error"`, `"warning"`, `"information"`, `"hint"`, `"off"`.

`"off"` suppresses the rule unless a higher-priority layer re-enables it. Unrecognised
message IDs are skipped with a warning in the log.

---

### `builtins.functions`

Additional functions recognised by the validator and completion providers. Keys are
`UPPER_CASE` function names.

```js
functions: {
    EMP_LOOKUP: {
        params:      [{ type: "STRING", label: "lookupCode" }],
        returns:     "STRING",                  // return type
        description: "..."                     // shown in completion documentation popup
    }
}
```

Required fields: `params` (array of per-parameter objects), `returns`.  
Optional per-parameter fields on each `params[i]`: `label`, `mode`, `role`.  
Optional top-level fields: `varArgs`, `description`.

Each `params[i].type` is a single ArchitectType string, or an array of ArchitectType strings
for a parameter that accepts a union (e.g. `[ "SHORT", "LONG" ]`).

`params[i].mode` is one of `"REF"`, `"VAR"`, `"VAR_init_not_required"`, `"VAR_destroy"`.
`"REF"` is the default — omit the field rather than authoring it explicitly.
`"VAR_init_not_required"` marks an out-parameter — the function writes into a caller-provided
variable that need not be initialised first.

`params[i].role` is a closed-vocabulary semantic tag for tooling. The current value is
`"path"` — read by the SEP_FOLDER portability lint to flag hard-coded path separators in
string-literal arguments. Omit the field when no role applies.

`varArgs` (boolean, default `false`) declares that the function accepts any number of trailing
arguments beyond `params`. `params` may be empty if no positions are known.

Valid type strings: `"STRING"`, `"SHORT"`, `"LONG"`, `"INTEGER"`, `"BIGINT"`, `"DOUBLE"`,
`"BOOLEAN"`, `"DATE"`, `"TIME"`, `"TIMESTAMP"`, `"UNKNOWN"`.

The reference module's `functionsRegistry.js` carries small samples for each of these shapes
(out-param, role, varArgs, pure-varArgs) — copy and adapt the one closest to your needs. The
authoritative schema is at
`architect-language-tools/builtins-data/schemas/function.schema.json`.

---

### `builtins.globalVariables`

Global variables that scripts may use without declaring. Keys are `UPPER_CASE` names.

```js
globalVariables: {
    EMP_SITE_CODE: {
        type:        "STRING",   // required — type string
        readOnly:    true,       // optional — see below
        description: "..."      // optional
    }
}
```

`readOnly` is optional. Set it explicitly on every supplementary entry for clarity:
- `true`  — set exclusively by the runtime / employer bootstrap; scripts must treat as read-only.
- `false` — writable by scripts; value changes are meaningful and expected.

Validators do not currently enforce `readOnly` on supplementary entries, but the field is
read by hovers and is the right place to record the contract regardless.

---

### `builtins.tables`

Table definitions injected into the table registry. Keys are two-character table codes
(upper-case). Each entry has two parts: `entry` (catalog metadata) and `structure`
(full index and field definition).

```js
tables: {
    ZZ: {
        entry: {
            name:     "Sample Employer Table",   // required string
            category: "Employer"                 // optional string
        },
        structure: {
            code:         "ZZ",                  // required — must match the key
            name:         "Sample_Employer_Table",
            recordLength: 64,                    // optional number
            helpRegistry: "ZZ_Table.htm",        // optional — links to the help page
            indexes: [                           // required array
                {
                    name:       "PK",            // "PK" for primary key; integer string for named indexes
                    modifiable: false,
                    duplicates: false,
                    segments: [
                        { position: 0, field: "ZZi_Identity", type: "BIGINT", length: 8 }
                    ]
                }
            ],
            fields: {                            // required object (may be empty)
                "hzzs_code": {
                    canonical:  "hZZs_Code",     // exact casing shown in hover/completion
                    type:       "STRING",
                    tableCode:  "ZZ"
                }
            }
        }
    }
}
```

Field handle keys are lowercase. The `canonical` value carries the mixed-case form that
the completion provider displays.

---

### `builtins.help`

Maps token names to help page filenames. Injected into the help registry so that hovering
over a supplementary function or table code opens the correct documentation page.

```js
help: {
    "EMP_LOOKUP":    "EmpLookup.htm",       // function help
    "EMP_SITE_CODE": "EmpSiteCode.htm",     // global variable help
    "ZZ":            "ZZ_Table.htm"         // table help (mirrors the table structure's `helpRegistry`)
}
```

Tokens may be function names, global variable names, or table codes. Both keys and values
must be strings; the loader does not enforce a particular extension or naming convention.

---

### `builtins.deprecatedSymbols`

Records deprecated identifiers and the replacement to surface in tooling. When a deprecated
name is referenced in code, ACU-SYM-001 fires with the message
*'{name}' is deprecated. Use '{replacement}' instead.*

```js
deprecatedSymbols: {
    EMP_OLD_FIELD:   "hEMPz_NewField",      // field handle migration
    EMP_GET_DESC:    "EMP_LOOKUP(code)",    // function migration (with call shape)
    EMP_LEGACY_SITE: "EMP_SITE_CODE"        // global variable migration
}
```

Keys are upper-cased. Replacement values are shown verbatim in the diagnostic and may be a
handle, a function call shape, a global name, or any short expression that helps the user
migrate.

---

## Loading in VS Code (companion extension)

The supplementary module is loaded by a thin companion VS Code extension. The companion
extension declares a dependency on the core plugin in its `package.json`:

```json
{
  "extensionDependencies": ["jibbius.arch-lang-tools"]
}
```

In the companion extension's `activate` function (CJS — VS Code extensions are CommonJS):

```js
function activate(context) {
    const core = vscode.extensions.getExtension('jibbius.arch-lang-tools').exports;
    core.registerSupplementary(require('./path/to/supplementary/index.js'));
}
```

The loader unwraps `.default` automatically, so the CJS `require()` result works without
any extra handling.

---

## Loading in the CLI

Add a `supplementaryModule` path to `.architect.json`:

```json
{
  "supplementaryModule": "./client-arch-lang-supplementary/index.js"
}
```

The path is resolved relative to the `.architect.json` file. On a developer workstation
this points to a local checkout of the supplementary module; in a CI/CD pipeline the
agent checks out the same repository and the path resolves identically.

The CLI imports the module at startup using a dynamic `import()`. No rebuild step is
required — the employer's module is loaded from source at runtime.

---

## `preferCore` flag

`registerSupplementary` accepts an options object with a `preferCore` boolean:

```js
core.registerSupplementary(supplementaryMod, { preferCore: true });
```

| `preferCore` | Conflict behaviour |
|---|---|
| `false` (default) | Employer entry overwrites the core entry. Use when the employer's registry is authoritative and more complete than the base distribution. |
| `true` | Core entry is kept; the conflict is logged as a warning. Use in tests or when integrating a module whose entries have not yet been validated against production function signatures. |

---

## `clearSupplementary` (testing)

The core module exports `clearSupplementary()` to reverse all mutations made by
`registerSupplementary`. Call it in `afterEach` when testing code that loads a
supplementary module:

```js
import { registerSupplementary, clearSupplementary } from
    "../../core/src/supplementaryLoader.js";

afterEach(() => clearSupplementary());
```

This removes injected functions, global variables, validators, and severity overrides,
restoring the core registries to their baseline state. Table codes added to
`KNOWN_TABLE_CODES` are also cleaned up.

---

## Folder structure

```
architect-language-tools-supplementary/
├── index.js                              default export — the supplementary module object
├── package.json                          ESM, no dependencies
├── jest.config.cjs                       test runner config
└── src/
    ├── builtins.js                       barrel re-exporting from the JSON loaders
    ├── builtins/
    │   ├── functionsRegistry.js          loader: reads functions/<NAME>.json
    │   ├── globalVariablesRegistry.js    loader: reads global-variables/<NAME>.json
    │   ├── tableRegistry.js              loader: reads tables/<CODE>.json
    │   ├── helpRegistry.js               loader: reads help-registry.json
    │   └── deprecatedSymbols.js          loader: reads deprecated-symbols.json
    ├── builtins-data/                    AUTHORING SOURCE OF TRUTH (JSON)
    │   ├── schemas/                      vendored, byte-identical copies of core schemas
    │   │   ├── function.schema.json
    │   │   ├── global-variable.schema.json
    │   │   ├── table.schema.json
    │   │   ├── help-registry.schema.json
    │   │   └── deprecated-symbol.schema.json
    │   ├── functions/<NAME>.json         one file per function entry
    │   ├── global-variables/<NAME>.json  one file per global variable
    │   ├── tables/<CODE>.json            one file per table (entry + structure unified)
    │   ├── help-registry.json            composite map: token → help page
    │   └── deprecated-symbols.json       composite map: old name → replacement
    ├── catalog/
    │   └── demoSeverityCatalog.js        sample DEMO-SEV-* catalog entries
    └── validators/
        ├── empStyleValidator.js          sample EMP-STY-001 validator
        └── demoSeverityValidator.js      sample DEMO-SEV-* validator
```

### Authoring with JSON + schemas

Every entry in `src/builtins-data/` carries a relative `$schema` ref (`../schemas/...`
from per-entry files, `./schemas/...` from composite files). VS Code and other JSON-aware
editors resolve these refs locally inside the supplementary repo, so authoring-time
validation works without a sibling checkout of `architect-language-tools/` — important
for air-gapped consumers.

The schemas are **vendored** copies of the core schemas, not an npm dependency. This
package and `architect-language-tools` are sibling VS Code extensions, not packages in a
runtime dependency relationship.

Three other artefacts are vendored from core alongside the schemas, for the same reason
(this tree must build standalone in the third-party hand-off scenario, with no sibling
core checkout): `src/builtins/_schemaValidator.js`, `src/builtins/_dataLoaders.js`, and
`tools/_dataProjectors.js`. The runtime loaders and `build.js` import these local
copies — never reach into the sibling tree.

To resync all vendored copies after a core change:

```sh
cd architect-language-tools-supplementary
npm run sync:core
git diff src/builtins-data/schemas/ src/builtins/_*.js tools/_*.js
git add . && git commit -m "chore(supplementary): sync vendored copies from core"
```

A drift test in core's suite (`test/unit/supplementarySchemaSync.test.js`) byte-compares
all four sets and fails CI on divergence.

### Where validation actually runs

The loader files in `src/builtins/*.js` **do not validate** — they just read JSON and
return the in-memory shape. Runtime validation happens centrally in core's
`supplementaryLoader.js` at registration time, against core's own schema copies. The
vendored copies in `src/builtins-data/schemas/` exist only for authoring-time editor
validation; the drift test guarantees they stay in step with core.

---

## Authoring your own module

1. Copy this package (or start from scratch) as a new directory alongside
   `architect-language-tools/`.
2. Replace the `ZZ` table, the `EMP_*` sample functions / variables / deprecated symbols, and
   `empStyleValidator` with your organisation's actual content.
3. Choose a diagnostic ID prefix that does not collide with `ACU-` (e.g. `ORG-STY-`,
   `ORG-INT-`).
4. Point `.architect.json` at your module's `index.js` for CLI use.
5. Wire up the companion extension for VS Code use.
6. Run `node --experimental-vm-modules node_modules/.bin/jest` from your module
   directory to run unit tests.
