Build apps
Writing manifests
The common shape of manifests (YAML), the kinds of definitions, file layout, validation, and CEL expressions.
This page explains how to write Hane definitions as manifests. Manifests are YAML files kept in Git. When you apply them with hanectl they become drafts, and they take effect when you activate a ReleaseBundle. The concepts behind definitions are covered in Definitions and releases.
Common shape
Section titled “Common shape”Every manifest has four parts: apiVersion, kind, metadata, and spec.
apiVersion: data/v1alpha1 # format version (<group>/v1alpha1)kind: Rule # kind of definitionmetadata: name: lease-end-date # lowercase letters, digits, hyphens. Up to 63 characters annotations: description: Calculates the end date from the start date and the term in monthsspec: target: LeaseContract field: end_date inputs: [start_date, term_months] expr: addDays(addMonths(record.start_date, record.term_months), -1)- Don’t write
statusor the revision in the file. Hane adds them on every apply. - Each kind’s
specis defined by a JSON Schema, and unknown keys are rejected. - You can put several manifests in one file, separated by
---.
Kinds of definitions
Section titled “Kinds of definitions”| apiVersion | kind | What it defines |
|---|---|---|
data/v1alpha1 |
Schema, Ontology, Rule, Calculation, Migration, Axiom, Aspect |
Types and fields, relations, rules and calculations, field renames and type changes, business knowledge and aspects |
workflows/v1alpha1 |
Workflow, Schedule |
How cases proceed, and daily triggers |
approvals/v1alpha1 |
AssignmentRule, DeciderPolicy |
Task assignment, and who decides external actions |
agents/v1alpha1 |
Agent, EvalSuite, TriageRule |
Agents, evaluations, triage rules |
documents/v1alpha1 |
DocumentType, ExtractionSchema, DocumentSource |
Document types, extraction schemas, external storage |
communication/v1alpha1 |
SenderIdentity, ContactPolicy, ContactRetention, CallBriefTemplate |
Sender identities, contact policies, contact retention, call brief templates |
integrations/v1alpha1 |
Connection |
Connections to external systems |
gateway/v1alpha1 |
Route, PublishedApi, ToolExposure, Consumer |
Routes to your APIs, and exposing them as agent tools |
browser/v1alpha1 |
BrowserFlow |
Steps for operating a portal |
platform/v1alpha1 |
Budget, RoleBinding, GitSource |
Budgets, role bindings, syncing from Git |
release/v1alpha1 |
EnvironmentBinding, SourcePolicy |
Names that differ per environment, rules for where definitions come from |
factory/v1alpha1 |
Application |
Apps served by Hane |
Settings that take effect immediately
Section titled “Settings that take effect immediately”The following kinds are settings. They are not bundled into a ReleaseBundle and take effect as soon as you apply them in each environment: Consumer, ContactRetention, DocumentSource, EnvironmentBinding, GitSource, RoleBinding, SenderIdentity, SourcePolicy. All other kinds become drafts and don’t take effect until a ReleaseBundle is activated. After apply, hanectl tells you which happened.
File layout
Section titled “File layout”Put each kind in its own directory, and list what goes into the ReleaseBundle in bundle.yaml. Bundle is read only by hanectl and is not sent to Hane.
Directorydefinitions/
- bundle.yaml
Directoryschemas/
- vehicle.yaml
- lease-contract.yaml
Directoryrules/
- …
Directoryworkflows/
- …
Directoryschedules/
- …
Directorypolicies/
- …
Directoryagents/
- …
Directorysenders/ settings; not listed in bundle.yaml resources
- …
apiVersion: release/v1alpha1kind: Bundlemetadata: name: vehicle-leasespec: resources: - schemas/ - rules/ - workflows/ - schedules/ - policies/ - agents/When you add a directory for a new kind, add it to resources too. If you forget, apply still succeeds, but the definitions are not included in the ReleaseBundle and never take effect.
Validation
Section titled “Validation”| When | Where | What is checked |
|---|---|---|
hanectl validate -f <dir> |
Locally | Shape (JSON Schema), references between definitions, CEL expressions. Secret names and term collisions are reported as warnings |
hanectl diff -f <dir> |
Hane | What apply would change. Flags changes such as widening what AI may approve |
hanectl apply -f <dir> |
Hane | Runs the same checks on the server, and saves drafts if they pass |
| Activating a ReleaseBundle | Hane | Whether per-environment names (EnvironmentBinding) resolve, and in stage and prod, whether evaluations (EvalSuite) pass |
Local validation uses the same checks as Hane, so running hanectl validate in CI catches mistakes before apply.
CEL expressions
Section titled “CEL expressions”Conditions and calculations are written in CEL. Expressions are limited to 1,000 characters and cannot use the current time, random numbers, or external reads and writes. The same input always produces the same value.
| Where | Variables |
|---|---|
Rule expr |
record (input fields only) |
Schedule forEach.where |
record, states, event (event.date is the trigger date) |
Workflow expressions (titles, branches, due dates, and so on) |
subject, states, case, steps, input |
where of GET /v1/data/records |
record, states |
Calendar dates are YYYY-MM-DD strings, calculated with these functions:
| Function | What it does |
|---|---|
addDays(date, n), addMonths(date, n) |
Adds days or months to a date |
daysBetween(a, b) |
Days between two dates |
isDate(v) |
Whether a value is a date string |
zonedDate(time, zone) |
Converts a recorded time into a calendar date in a time zone |
minOf(a, b) |
The smaller of two values |
moneyText(m) |
Formats an amount with the currency’s decimal places |
# Every morning, open a case for each active contract whose notice deadline is within 90 daysapiVersion: workflows/v1alpha1kind: Schedulemetadata: name: daily-expiry-scanspec: every: day at: "07:00" timezone: Asia/Tokyo forEach: type: LeaseContract where: record.status == "active" && record.notice_due != null && daysBetween(event.date, record.notice_due) <= 90 workflow: lease-expirystates holds each field’s value state (confirmed, estimated, pending_inquiry, unknown). To stop a process from moving on with unconfirmed values, branch on something like states.end_date != "confirmed".