Lua v4 Script Configuration
Lua v4 configuration is static bundle metadata plus a validated value file. A
bundle declares a small flat schema without executing Lua. Hexis validates the
schema before a run, captures one complete configuration snapshot when that run
starts, and gives Lua fresh table copies through hexis.config.snapshot().
The backend and Lua read call are implemented in the source foundation. They remain non-executable in these docs until a named distributed build passes controlled acceptance. The current Galatea UI adapter still has no customer settings editor, so the persisted update path is not yet exposed through that module's settings panel.
Exact manifest dialect
configSchemaVersion is a positive integer owned by the bundle. The
configSchema root has exactly four fields:
{
"type": "object",
"additionalProperties": false,
"required": ["route", "competition_mode", "max_azaleas"],
"properties": {
"route": {
"type": "string",
"title": "Fig Tree Route",
"default": "official:Galatea_Foraging",
"format": "hexis-route",
"maxLength": 160
},
"competition_mode": {
"type": "boolean",
"title": "Agatha Contest Mode",
"default": true
},
"max_azaleas": {
"type": "integer",
"title": "Max Azaleas Between Trees",
"default": 6,
"minimum": 0,
"maximum": 15,
"step": 1
}
}
}
type must be object, and additionalProperties must be false. A schema
has at most 32 properties. Each property name must match
[a-z][a-z0-9_]{0,63}. required must list every property exactly once, with
no missing, duplicate, optional, or undeclared names. An empty schema uses
empty required and properties values.
Every field requires a nonblank title of at most 80 characters and a valid
default. Only these scalar types and keywords exist:
| Type | Required keywords | Optional keywords |
|---|---|---|
boolean | type, title, default | none |
integer | type, title, default, minimum, maximum | step |
number | type, title, default, minimum, maximum | step |
string | type, title, default, maxLength | minLength, enum, format |
Unknown or missing keywords reject the bundle. Arrays, nested configuration objects, nulls, descriptions, and undeclared JSON Schema features are not part of this dialect.
For numeric fields, the minimum, maximum, default, and optional step must be finite. The minimum cannot exceed the maximum. A step must be positive, and both defaults and saved values must align to it from the minimum. Integer schema values must be exact integers no larger in magnitude than 9,007,199,254,740,991.
For strings, maxLength is an integer from 1 through 4,096. Optional
minLength is from 0 through maxLength. An enum contains 1 through 64
unique, nonempty strings, and its default must be one of them. enum and
format cannot be combined.
The only format is hexis-route. It limits maxLength to 160 and accepts this
shape:
[a-z0-9._-]+:[A-Za-z0-9._/-]+
All defaults are validated by the same rules as saved values. A bundle with an invalid schema never starts Lua.
One immutable snapshot per run
The public call takes no arguments and requires no capability:
local config = hexis.config.snapshot()
local selected_route = config.route
Before creating the run, Hexis loads one complete validated snapshot for that
bundle's stable script ID and configSchemaVersion. The host keeps that
snapshot immutable for the run. Every call to hexis.config.snapshot() returns
a fresh mutable Lua table copied from it. Changing a returned table cannot
change the run snapshot, another call, the stored settings, or another run.
There is no Lua setter, live config event, shared mutable table, capability, or filesystem access. A successful settings update applies to the next run. It does not alter a script that is already running. A script that wants new values must stop and start a new run.
Stable identity and persistence
Configuration is keyed by the manifest's stable ScriptId, not its display
name, version, file hash, installation origin, or UI position. A script ID uses
lowercase namespace:name; each side matches [a-z0-9][a-z0-9._-]*.
The production file path is:
<Fabric config directory>/hexis/script-v4/<namespace>/<name>.json
For hexis:galatea-fig, that is
<Fabric config directory>/hexis/script-v4/hexis/galatea-fig.json. The file
contains exactly:
{
"schemaVersion": 2,
"values": {
"route": "official:Galatea_Foraging",
"competition_mode": true,
"max_azaleas": 6
}
}
The stored file is limited to 64 KiB. values must contain exactly every
schema property and only scalar booleans, strings, or numbers accepted by the
current schema.
A missing, empty, oversized, malformed, non-object, wrong-version, incomplete,
extra-field, or otherwise invalid file produces the schema defaults. Hexis does
not rewrite, delete, rename, or automatically migrate that file while reading
it. This avoids destroying evidence or replacing an old configuration merely
because a new bundle changed its schema. Bundle authors must increment
configSchemaVersion when the schema meaning changes. There is no automatic
value migration in this foundation. The production store caches its current
value by script ID and schema version, so manually editing the JSON while Hexis
is running is not a supported update path and is not guaranteed to be observed.
Validated atomic updates
The backend validates the entire candidate before writing. It serializes a
sibling .tmp file, flushes the file contents and metadata, then attempts an
atomic replace of the final JSON. If the filesystem does not support atomic
move, it falls back to a normal replacement. The in-memory value changes only
after the replacement succeeds. A failed write keeps the previous cached
snapshot and removes the temporary file on a best-effort basis.
This is strong protection against a partially written JSON file, but it is not an absolute power-loss guarantee. The implementation does not fsync the parent directory after the rename, and the non-atomic fallback is necessarily weaker. Documentation and UI must not claim stronger durability than the underlying filesystem provides.
Configured official bundles
Galatea config schema version 2 has exactly three required settings:
| Field | Exact contract | Current meaning |
|---|---|---|
route | hexis-route string, default official:Galatea_Foraging, maximum length 160 | Selects the route used for both tree and azalea entry filtering |
competition_mode | boolean, default true | Enables the bounded azalea and Tadpole detours; false runs the tree scheduler only |
max_azaleas | integer 0 through 15, default 6, step 1 | Caps successful azalea harvest actions between successful nonempty trees; 0 disables azalea detours |
The name competition_mode does not imply contest detection, score tracking,
or automatic stopping at a contest maximum. It is only the scheduler toggle
described above. Galatea reads the config once and combines config.route with
the workload's tree or azalea route filter:
local WORKLOAD = hexis.content.pack({name = "workload"})
local CONFIG = hexis.config.snapshot()
local function route_request(filter)
return {
route = CONFIG.route,
kind = filter.kind,
entry_block_types = filter.entry_block_types,
}
end
The hexis.galatea_fig workload is schema version 5. Its tree_route and
azalea_route objects contain only kind and entry_block_types; they no
longer contain a route ID. This keeps the customer-selected route in one
authoritative configuration field instead of duplicating it in validated
workload data. Its tree and azalea selectors now contain exact namespaced block
IDs mapped to integer priorities, matching the v4 mining request directly. The
old pack-owned max_azaleas_between_trees field is also gone; the validated
max_azaleas customer setting is authoritative.
The reduced Sven bundle has one required flee_health integer setting. Its
default is 10, minimum 5, maximum 20, and step 5. The entrypoint passes that
snapshot value to combat.engage.stop_below_health_percent and stops its run
on the non-retryable player_low_health result. This is a stop threshold, not
an automatic healing or retreat implementation.
The reduced Experiment Table bundle uses config schema version 2 with exactly
three required booleans: solve_chrono, solve_ultra, and
solve_superpairs. Each defaults to true. The entrypoint reads one snapshot
and exits before subscribing or clicking when the already-open experiment's
solver is disabled. There is no auto_renew property; renewal and currency
spending remain excluded rather than hidden behind a setting.
Marketplace relevance and remaining gates
The implemented content-source boundary can list a bundle's stable ID and strict configuration schema, along with its source, compatibility, and descriptive install state, then prepare one exact selected version. That list-and-prepare seam can be implemented by a future marketplace source without changing the Lua runtime. Marketplace download, install, update, removal, rollback, publisher verification, catalog trust, conflict handling, and settings migration are not implemented yet.
Before configuration is public, a named distributed build still needs live acceptance for the settings editor, successful and failed persistence, restart loading, schema-version mismatch, defaults, and proof that an active run stays unchanged until restart. The backend alone does not prove that UI workflow.