What a declaration is
Your rules do not live in prompts or in code paths. They live in declarations, held in Declaration Studio in elan1 Govern. A declaration is configuration, not a build: an operator who knows the process can read every block of it.
- Held in
- Declaration Studio, in elan1 Govern. Not Decision Studio, which belongs to elan1 Decide and holds questions, not the rules a write must pass.
- Registered by
define, the first KernelPort call. Registering a version is itself a governed write under dual control.- Blocks
- Seven: identity, shape, permissions, write_types, gates, proof_binding and channels.
- Versioned
- Every registered change is a new version. Old versions are never edited.
- Named on every record
- F06 declaration_id and F07 declaration_version, next to F08 policy_version.
- Evaluated by
- evaluate() at rung 4, with versioned rules and no model on the allow path.
- Status
- Available · part of L1 Guarded
The seven blocks
The block names are fixed. What each block says is below; the exact keys and value types inside each block are in the customer reference; the ones shown here are illustrative.
| Block | What it says |
|---|---|
1identity | What this is: name, owner role, residency domain, system of record. Read at: Rung 1 IDENTITY |
2shape | The record type, its fields and how each is classified. Read at: Rungs 3 and 4 |
3permissions | Who may change it, by actor kind, role and minimum autonomy. An agent may never approve. Read at: Rung 6 AUTONOMY |
4write_types | The write types, each with a blast radius, dual control, ordered gates and a reversal plan, or irreversible: true. Read at: Rungs 2 and 5 |
5gates | Which of the nine gates run, with their parameters and refusal codes. Read at: Rung 3 PRE-GATES |
6proof_binding | Which fields the proof commits to, and how they are canonicalised. Read at: Rung 8 PROOF |
7channels | Web, mobile, messaging, API, MCP, voice: what each carries, and in which mode. Read at: The connection |
Where each block acts on the Right Path
A write intent climbs the Right Path from rung 0b STOP to rung 9 METERED. Each block is read at a known rung, so a refusal can always be traced to the block that caused it.
| Block | Read at | What it decides there | On the record |
|---|---|---|---|
| identity | Rung 1 IDENTITY · gate G2 Residency | Which tenant and residency domain the process belongs to, and which system of record it writes to. | F01 · F06 |
| write_types | Rung 2 WRITE DOOR · rung 5 GUARDRAIL | The door opens only for declared write types. The guardrail measures the blast radius against the declared limit. | F05 |
| gates | Rung 3 PRE-GATES | Which gates run, in which order, with which parameters, and the refusal code each emits. | Refusal code, beside the record |
| shape | Rung 3 PRE-GATES · rung 4 evaluate() | The record type and how each field is classified, which tells consent and retention gates where personal data may sit. | F12 · F13 |
| permissions | Rung 6 AUTONOMY | Who may propose and who may approve, by actor kind, role and minimum autonomy. Above the ceiling, a named person decides. | F09 · F10 · F11 |
| proof_binding | Rung 8 PROOF | Which fields the proof commits to, and how they are canonicalised. It never adds a field: the record stays at sixteen. | F14 |
| channels | Rung 1 and the connection | Which channels may carry the process, and in which mode. Only a Mode A channel is enforced. | Mode mark, in every export |
Every rung reads the declaration version that was in force when the intent arrived. See The Right Path for every rung.
An illustrative declaration
The month-end close declaration for Kolam Components*, at version 7: the declaration that specimen record seq 1042 was checked against. It is written as JSON here to show the shape of the seven blocks. The serialisation format is in the customer reference.
{
"declaration_id": "decl_month_end_close",
"declaration_version": 7,
"identity": {
"name": "Month-end close",
"owner_role": "financial_controller",
"residency_domain": "in",
"system_of_record": "erpnext_hosted"
},
"shape": {
"record_type": "journal_entry",
"fields": [
{ "name": "posting_date", "class": "business" },
{ "name": "accounts", "class": "business" },
{ "name": "amount_inr", "class": "business" },
{ "name": "narration", "class": "may_contain_personal_data" }
]
},
"permissions": [
{ "actor_kind": "agent", "role": "close_agent", "may": "propose", "min_autonomy": "A1" },
{ "actor_kind": "human", "role": "accounts_executive", "may": "propose" },
{ "actor_kind": "human", "role": "financial_controller", "may": "approve" }
],
"write_types": [
{
"write_type": "journal_entry.post",
"blast_radius": { "max_amount_inr": 500000, "max_records": 1 },
"dual_control": true,
"gates": ["G2", "G9", "G4", "G6"],
"reversal_plan": "compensating_entry"
}
],
"gates": {
"G6": { "budget_period": "month", "on": true },
"G8": { "on": false, "decision_record": "dr_specimen_0007" },
"G9": { "preconditions": ["period_open", "no_duplicate_number"] }
},
"proof_binding": {
"commit_fields": ["posting_date", "accounts", "amount_inr", "narration"],
"canonicalisation": "<per the record-shape reference>"
},
"channels": {
"web": { "mode": "A" },
"mcp": { "mode": "B" },
"api": { "mode": "B" }
}
}*Fictional company; all data is illustrative. Gate identifiers follow the kernel register (G1 Consent to G9 Precondition). The six gates that are never skipped run whether or not a write type lists them; the list sets their order. G8 Schedule is switched off here, which needs a decision record.
Reading it
- The close agent may only propose, at A1 or above. It may never approve, and no permission can grant that.
journal_entry.postis the only door. Any other write type is refused at rung 2.- Dual control is on, so a second named person approves every posting. The approver is never the asker.
- A posting above ₹5,00,000 or touching more than one record exceeds the blast radius and is refused at rung 5. (Illustrative limits.)
- The web channel is Mode A, where elan1 Work holds the write credential, so writes there are enforced. MCP and API are Mode B: evaluated and recorded.
Versioning
A declaration is never edited in place. A change is registered as a new version, through define, and the old version stays exactly as it was, because records still point at it.
Draft.
The rules are written in Declaration Studio, block by block. A draft governs nothing.
Register at an explicit version.
defineregisters the declaration at a stated version and returns a content-addressed digest. Registering is itself a governed write under dual control, so it is held for a second named person and leaves its own proof record.In force.
From then on, write intents for the process are evaluated against that version, and each record carries it in F07.
Change.
A new version is registered the same way. Writes after it name the new version; every earlier record still names the version it ran under.
Replay.
The same intent, under the same versions and the same as-of time, gives the same decision. That is what makes an old record checkable against old rules.
define(decl_month_end_close, version 8) # registering is itself a governed write
→ dual control: a second named person must approve the new version
← escalated waiting for usr_specimen_controller_2 at rung 6
← committed declaration_version 8 · digest sha256:7a1d…c03e · proof record sealed
assert(journal_entry.post, …) # every later write names the version it ran under
← committed F06 decl_month_end_close · F07 8 · F08 2027.06.1- Pin versions. A production key cannot ask for “latest”; every decision records the versions it used. This is conformance test C13.
- Wide autonomy costs more control. Where a declaration lets an agent act at A5, at tenant scope, the declaration itself needs dual control.
- Policy has its own version. F08 policy_version records the version of the policy rules evaluate() ran, separately from the declaration version, so either can move without hiding the other.
How a record names its declaration
Four fields tie a record to its rules. Here they are on specimen seq 1042:
| Field | Name | Specimen (seq 1042) |
|---|---|---|
| F05 | write_type | journal_entry.post |
| F06 | declaration_id | decl_month_end_close |
| F07 | declaration_version | 7 |
| F08 | policy_version | 2027.06.1 |
Illustrative specimen. Not customer data.
To answer “which rule allowed this?”, read F06 and F07, open that exact version, and find the write type in F05. Because each of these fields is inside the hashed preimage, changing which declaration a record claims would break the chain. See the preimage.
From L2, the evidence bundle carries a declarations/ member, so the rules a record names travel with it and can be read offline, without us.
Decision Packs compile to the same blocks
elan1 Decide’s Decision Packs bundle the schemas, context, thresholds and write types for one use case, such as claims first notice of loss or AML alert triage. A pack holds schemas from Decision Studio, a calibration plan, routing tiers, review queues and the write types it may propose, and compiles to the same seven blocks. There is one rule format across the layers, and a pack never becomes a second kernel: its writes still go to elan1 Govern as intents, in Mode B.
What the customer reference covers
Serialisation format and keys inside each block
The block names are fixed. The keys, value types and file format shown above are illustrative.
In the customer reference
How the content-addressed digest is computed
define returns a digest of the registered version; its canonical form is part of KernelPort v1.
In the customer reference
Pack gate names and the kernel's nine gates
Accounting packs name gates by what they check in the books, such as G1 Ledger and G4 GST. How each pack gate maps onto the nine kernel gates is listed per pack.
In the customer reference
Intents held across a version change
What happens to a write held for approval when a new version is registered before the approver acts.
In the customer reference
Refusal codes per gate
Codes on this site are illustrative; the closed register is part of KernelPort v1.
In the customer reference