rototo
DocsReference
Reference

Custom Lua Lint Reference

Built-in lint protects rototo's workspace contract. Custom Lua lint protects local policy: the constraints that matter in your domain and are worth checking before a workspace is released.

Custom lint files live under lint/*.lua.

Minimal File

function register(lint)
  lint:on({
    stage = "value",
    entity = "value",
    field = "value.projects",
    rule = {
      id = "operations/account-project-limit",
      title = "Account project limit is outside policy",
      help = "Keep configured project limits between 1 and 100.",
      severity = "warning",
    },
    handler = "check_projects",
  })
end

function check_projects(ctx)
  local value = ctx.target.selected
  if type(value) == "number" and value > 100 then
    return {
      { message = "project limit is above the operations policy" }
    }
  end
  return {}
end

The file must define register(lint). Registration happens before custom handlers run.

Registration

lint:on accepts a registration table:

FieldRequiredMeaning
stageYesproject, reference, value, graph, or policy.
entityYesworkspace, qualifier, variable, value, or schema.
fieldNoNarrower field selector for the target.
ruleYesRule metadata table.
handlerYesName of a callable Lua function in the same file.

Rule metadata:

FieldRequiredMeaning
idYesCustom rule id in <authority>/<rule-id> form.
titleYesShort title used in catalogs.
helpYesGuidance shown with diagnostics.
severityNoerror or warning; defaults to error.

If the same custom rule id is registered more than once, title, help, and severity must match.

Field Selectors

Supported selectors:

EntityFields
workspaceextends
qualifierid, description, predicates
variableid, description, type, schema, values, resolve
valuekey, value, value.<json.path>
schemajson, json.<json.path>

For value.<json.path> and json.<json.path>, path components must use lowercase ASCII letters, uppercase ASCII letters, digits, _, or -.

Handler Context

Handlers receive one argument:

function handler(ctx)
  -- ctx.stage
  -- ctx.entity
  -- ctx.target
end

ctx.target depends on the registered entity.

For value targets:

{
  "kind": "value",
  "name": "enterprise",
  "value": {},
  "origin": { "kind": "inline", "doc": 4 },
  "selected": {},
  "variable": {
    "id": "account-limits",
    "uri": "variable://account-limits",
    "path": "variables/account-limits.toml"
  }
}

selected is the narrowed field when a value.<json.path> selector is used.

For schema targets, selected is the narrowed JSON Schema field when a json.<json.path> selector is used.

Handler Return

Handlers return a list of diagnostics:

return {
  {
    message = "configured value violates local policy",
    field = "value.projects",
  }
}

message is required. field is optional and can narrow the diagnostic location within the registered target.

Returning nil or an empty list means no diagnostics.

Lua Runtime Limits

Custom lint runs in a restricted Lua VM:

Available standard libraries are table, string, UTF-8, and math.

Custom lint should be deterministic and local to the workspace data it receives.