0. What Fudgel Is

Fudgel is a meta-language of intent. It is not a general-purpose programming language. It does not compute, render, allocate memory, or manage time. It describes the structure, logic, and data flow of an application in three distinct sub-languages — each with a single job — and compiles that description directly to bare-metal instruction sets with no intermediate abstraction layer.

The architecture is three layers, each completely agnostic to the others:

DSLLayerSingle Responsibility
BijaStateStores all state as a static entity tree. No logic, no operations. Human-readable names are discarded at runtime — every entity becomes a fixed memory address.
RumusLogicPure, finite functions that operate exclusively in registers. Reads no memory, writes no memory. Everything it touches is gone when it returns.
ViestiWiringConnects Bija addresses to Rumus functions and writes results back to Bija. Supports sequential and parallel execution through a two-symbol syntax.

Everything else — type checking, validation, timing, rendering, layout, animation, file I/O, encryption — is handled by domain-specific libraries. Each library is a domain-scoped semantic abstraction that compiles directly to Bija entities, Rumus functions, and Viesti signals. There is no distinction at the machine level between library code and author code.

Fudgel programs are deterministic by construction. Because Bija is fully resolved at load time, Rumus functions are pure, and Viesti resolves signals in declaration order, the same input state always produces the same output. Non-determinism can only be introduced explicitly through a library.

Core Law: if you need logic, write a Rumus function. If you need to remember something, write it to a Bija entity. Viesti connects the two. Nothing else exists.

0.1 Design Philosophy

Fudgel exists because the dominant software stack was never designed to be a universal application runtime. It became one through historical accident. The result is that applications with completely different constraints end up built on infrastructure designed for document delivery over a network.

The standard response has been more abstraction: frameworks, transpilers, runtimes-within-runtimes. These abstractions treat real symptoms — GC prevents use-after-free, ORMs prevent injection — but they are applied universally at a cost paid even when the original problem does not apply. A local game does not need GC. A simulation does not need an ORM. The tax is paid regardless.

Fudgel is closer to a Turing-complete algorithm description format than a conventional programming language. It says not "here is how to program this CPU" but "here is the algorithm — these are the steps, these are the dependencies, these can run in parallel, these cannot." The same source could run on RISC-V today, a GPU tomorrow, and a substrate that does not exist yet next decade — as long as someone has written the library that maps Rumus operations to that substrate's primitives. The algorithm is the stable thing. The execution target is not.

0.2 The False Tradeoff

Language design has historically operated on an assumed spectrum: human-readable on one end, machine-efficient on the other. Fudgel's claim is that this tradeoff is false. A sufficiently honest representation of intent is also a sufficient representation for direct compilation, because intent at the right level of granularity is exactly what the machine needs.

Bija describes memory layout. Rumus describes operations. Viesti describes execution order and data movement. That is not a simplification of what a computer does — it is an honest description of it in legible syntax. Legibility does not require abstraction. It requires good notation.

The key reframe: the decisions Fudgel asks a developer to make are not performance decisions. They are truth claims about the program. ~ means this depends on that. ~~ means these have no dependency on each other. {immutable} means this value never changes. !5 means these instances are identical in structure. The hardware uses these truths to execute correctly and efficiently — but the developer is not managing the hardware. They are describing their program accurately.

0.3 Error Handling and Safety

Fudgel is designed to hard crash on invalid runtime state. There is no exception system, no error monad, no try/catch. If a program reaches an invalid state at runtime, it stops. This is deliberate: runtime error handling is a secondary system bolted onto a primary system that already failed to prevent the error. Preventing the error is the correct investment.

Prevention happens at two levels. First, the library compilation pass — Ehto and other libraries inspect the Bija entity tree before the PAF is emitted, validating constraints and flagging missing references as compile-time errors. Second, the IDE's visualiser surfaces structural issues before a build is attempted. The hard crash is a last resort, not a first line of defence.

0.4 The IDE is Load-Bearing

The Fudgel IDE is not a convenience tool. It is a core part of the design thesis. The signal graph, the entity tree, and the compilation pass results are all structures that are intuitive when visible and intimidating when read as text. The WYSIWYG visualiser is what makes hardware-level explicitness accessible without being assembly-level painful.

The language and IDE should ship together. A language that requires a visualiser to be ergonomic but ships without one will be correctly perceived as hostile.

0.5 Target Domain

Fudgel's natural first audience is game and interactive application development. That community already thinks in ECS, already cares about determinism and performance, and is already frustrated with the abstraction overhead of existing engines. The longer-term target is any application that does not require the web stack's specific capabilities but ends up using it anyway due to ecosystem gravity.

0.6 Fudgel and AI Tooling

Fudgel's signal graph is the actual runtime topology, not an abstraction over it. Every data dependency is a declared edge. Every state mutation is a named Viesti signal. There is no hidden control flow to infer. A model reading a Fudgel application sees the real execution structure, not a representation of it.

What AI coding tools actually do is manage dataflow. A model pattern-matches on relationships between inputs, transformations, and outputs. Fudgel makes that graph literal. A Viesti graph is structurally closer to how a transformer reasons about information movement than a for-loop is. This makes Fudgel a better substrate for AI reasoning than almost anything currently in production use.

1. The Trinity

Fudgel is composed of exactly three sub-languages, each with a single responsibility. No sub-language can perform the role of another. This separation is enforced by the syntax of each sub-language: a Bija file contains no expressions, a Rumus function contains no storage writes, a Viesti graph contains no conditionals.

DSLSingle ResponsibilityWhat it cannot do
BijaStore state and structureExecute logic or route data
RumusCompute pure functionsStore state or route data
ViestiRoute data between addressesStore state or execute logic

A file's type is defined by its Trinity imports:

File TypeRequired ImportsDescription
Application@Bija @Rumus @ViestiComplete executable. First signal under @Viesti is the entry point.
Library@Bija @RumusTemplates and functions. No @Viesti — wiring is the consumer's concern.
Data file@BijaPure state declaration. No logic, no signals.

2. Bija — The State Layer

2.1 Purpose

Bija is the entire heap of a Fudgel application. It is the only place where state persists across function calls. At runtime, every human-readable name in a Bija file is discarded. The load-time pass assigns each entity and property a fixed memory offset. All subsequent references become raw pointer arithmetic.

This is the key distinction between Bija and general data formats like JSON or PKL. Those formats describe what exists. Bija describes what exists and how to derive everything else from it, purely through structure, with no runtime cost.

2.2 Core Rules

  • Everything is entities and properties.
  • Every property has exactly one value.
  • Indentation denotes hierarchy.
  • The parser has no type system, no logic, and no operations. Meaning arises from structure, not syntax.
  • Bija values are always atomic. A value is a literal, a reference, a shorthand token, or empty. It is never an expression. damage: #attack + 20 is a syntax error.
  • All entities are mutable by default. The {immutable} flag is a hint to the emitter. Immutable entities are inlined as immediates at compile time and assigned no memory address.
  • All shorthand — commas, !, positional pipes, inline sub-entity — resolves at parse time. Nothing from shorthand syntax survives into the PAF.

2.3 Syntax Reference

Entities

EntityName;
  SubEntity;

Named containers. End with a semicolon. Indentation denotes parent-child hierarchy.

Properties

propertyName: value

Always name: value. Value is a literal, a reference, a shorthand token, or empty. Never an expression.

Empty Value

propertyName: _

The explicit empty value. A property with no value is not null — it is empty by declaration.

References — #

#EntityName
#EntityName.property
#list.0           (ordered index)

# means these two things are the same thing. A semantic alias for the author's benefit. The compiler resolves it to the same memory address as the target and removes it. No indirection survives into the compiled output.

Value Copy — =

displayHealth: =Player.health

Copies the referenced property's value at parse time. The source is forgotten after copying. Not an alias, not a live link — if the source changes at runtime, this property does not change with it.

Instantiation

NewThing: #TemplateName

Copies the referenced entity's full structure at parse time, including all sub-entities and properties. Not a link. The copy is fully independent after instantiation.

Positional Shorthand — | v1, v2 |

Stats | strength, agility, luck |;     (inline sub-entity)
Sword: #TemplateItem | 15, 3 |         (template override)

TemplateItem;
  value: 0
  weight: 1
  durability: 100

Sword: #TemplateItem | 15, 3 |         (overrides value and weight — durability stays 100)
Shield: #TemplateItem | 5, 8, 200 |    (overrides all three)

Multiplier — !N

enemies: #Enemy !5

(compiler expands to)
enemies;
  Enemy_0: #Enemy
  Enemy_1: #Enemy
  Enemy_2: #Enemy
  Enemy_3: #Enemy
  Enemy_4: #Enemy

Stamps out N independent instances at parse time. The count must be a literal integer. Each instance is addressable as #enemies.0 through #enemies.4. Combinable with positional shorthand:

wave: #Enemy !5 | 200, aggressive |

Anonymous Lists — - and *

(ordered)
loot: -
  gold
  gem
  potion
(expands to: loot;  0: gold  1: gem  2: potion)

(unordered)
flags: *
  active
  visible
  collidable
(expands to: flags;  active: _  visible: _  collidable: _)

The - marker creates numbered sub-entities with their values. The * marker creates named sub-entities with empty values — the names are the data. Both resolve at parse time. Neither construct exists in the compiled output.

Metadata Booleans — {flag1, flag2}

health: 100 {required, unique}

(expands to)
health: 100
  required: true
  unique: true

Only booleans belong inside {}. The words required, unique, immutable and any others are not part of the Bija language — their meaning is supplied entirely by the library that reads them during the compilation pass.

Literal Strings — [...]

propertyName: [literal text content
that may span multiple lines]

Comments

(this is a comment and is ignored by the parser)

Parentheses delimit comments in all three DSLs. May appear anywhere, including inline. Nesting is supported.

2.4 Reference Semantics

SyntaxNameBehaviour
#Entity.propertyAliasCompiler resolves to same address. Reference removed. No runtime indirection.
=Entity.propertyValue copyValue copied at parse time. Source forgotten. Independent at runtime.

Neither syntax creates a live link. Bija is fully static after load. The only mechanism that reads or writes Bija state at runtime is a Viesti signal.

2.5 @ Declarations and File Types

@ has one universal meaning: bring this into scope. Every Fudgel file must declare its imports explicitly. Trinity imports also act as section markers — declarations following a Trinity import belong to that layer's scope. Import order is declaration order is compilation-pass order.

@Bija
@Ehto
Player;
  health: 100 {required}

@Rumus
? clamp{x, min, max};
  belowMin: x < min ? min : x
  result: belowMin > max ? max : belowMin
  ~result

@Viesti
Player.health ~ clamp ~ Player.health

2.6 Full Bija Example

@Bija
@Ehto

Player;
  name: Kael
  health: 180 {required}
  mana: 90 {required}
  startingHealth: =Config.maxHealth
  inventory;
    loot: -
      gold
      gem
      potion
    equippedItem: #loot.0
  stats;
    strength: 10
    agility: 8

Enemy;
  health: 80
  behaviour: passive

TemplateItem;
  value: 0
  weight: 1

Sword: #TemplateItem | 15, 3 |
  durability: 100

Scene;
  basicEnemies: #Enemy !5
  eliteWave: #Enemy !3 | 200, aggressive |

Config;
  maxHealth: 100 {immutable}
  version: 1 {immutable}

UI;
  shownHealth: =Player.health

3. Rumus — The Logic Layer

3.1 Purpose

Rumus is a pure transformation notation. A Rumus function takes named inputs, applies a transformation, and exposes outputs. It has no side effects, no memory allocation, no loops, and no branching. It describes what a computation is, not what a substrate does to perform it.

Rumus core defines the shape of transformation — bind, compose, apply, select — without assuming anything about what kind of values are being transformed or what hardware will execute the transformation. There is no optimisation pass. Performance is a direct product of design quality.

3.2 Rumus Core — Transformation Primitives

PrimitiveDescription
bindGive a value a name for use within a function scope. Parameters are bindings.
applyPass a value to a function and receive its output. Function calls are applications.
composeThe output of one function becomes the input of another. The ~ output feeds the next binding.
selectGiven multiple values, produce one. The conditional shorthand expands to this.

These four primitives are sufficient for Turing completeness without recursion, given that iteration is handled by Viesti signal cycles.

3.3 @rumus-riscv — Current Working Target

Rumus operationRISC-V instruction
x + yADD
x - ySUB
x * yMUL
x / yDIV
x % yREM
x = yBEQ
x < yBLT
a ? b : cBranch + label

The bitwise operations (&, |, ^, ~, >>, <<) are hardware-specific and belong in a systems target library. The ternary a ? b : c is shorthand for a Rumus core select operation — it expands at emit time. Rumus has no branching. Branching is Viesti's job.

3.4 Standard Operation Set

Arithmetic

OperatorDescription
x + yAddition
x - ySubtraction
x * yMultiplication
x / yDivision
x % yRemainder — divisibility checks and wrapping
-xUnary negation — distinct from binary subtraction

Comparison

OperatorDescriptionNotes
x = yEqualSingle = is equality in Rumus, not assignment
x != yNot equal
x < yLess than
x > yGreater than
x <= yLess than or equal
x >= yGreater than or equal
Equality in Rumus is a single =. It is a comparison, not assignment. Bija uses = for value copy; Rumus uses it for equality testing inside expressions.

Boolean

OperatorDescription
a & bBoolean AND — both true
a | bBoolean OR — at least one true
!aBoolean NOT — inverted truth value

Conditional

a ? b : c

Produces a value — does not route behaviour. Valid only when both branches produce values of the same kind through pure transformation of inputs. Any selection that routes to meaningfully different behaviours belongs in Viesti.

Formal Logic — Assertions

Assertions are statements about relationships between truth values scoped to function evaluation. They are not expressions — they are constraints that must hold. If an assertion fails, execution halts with the function path and the specific assertion that failed.

OperatorNameMeaning
a -> bImplicationIf a is true, b must be true
a <-> bBiconditionala is true exactly when b is true
a ^ bExclusive orOne or the other, never both
a !& bNANDNot both simultaneously true — mutual exclusion
a !| bNORNeither is true — joint absence
a <- bConverse implicationIf b is true, a must be true

3.5 Function Declaration and Body

? functionName{param1, param2};
  intermediate: param1 + param2
  param1 > 0 -> intermediate > param1   (formal logic assertion)
  anotherStep: intermediate * 2
  ~anotherStep

The ? prefix marks a Rumus function entity. Parameters use {} boolean shorthand — each expands to a sub-entity with value true. Viesti overwrites them with actual values before execution. A function cannot run unless all parameters have been supplied. Body property names are discarded at emit time and become positional register slots. A function body that exceeds available register capacity is undefined behaviour — decompose the function.

3.6 Output Declaration — ~

~value
~value1, value2

~ declares which values are exposed to Viesti. These are the only externally addressable values on a function entity. All other body properties are ephemeral and do not exist in the PAF.

3.7 The Function Address Surface

AddressDescription
functionName.x (parameter)Viesti writes here to supply data. Overrides the default true before execution.
functionName.lo (~ output)Viesti reads here to route the result onward after the function returns.

3.8 Calling Another Function

? normalise{x};
  clamped: #clamp | x, 0, 100 |
  result: clamped / 100
  ~result

A Rumus function may call another using Bija instantiation syntax. Single caller: the called function is inlined — its body is substituted directly into the caller's instruction sequence. Multiple callers: the function is emitted as a subroutine. This is a determinism guarantee, not an optimisation.

3.9 # in Rumus

(author writes)
? applyScale{value};
  result: value * #Config.scaleFactor
  ~result

(compiler expands to)
Config.scaleFactor ~ passthrough ~ applyScale.scaleFactor

? applyScale{value, scaleFactor};
  result: value * scaleFactor
  ~result

# inside a Rumus function body means the value comes from a live Bija address. The compiler expands this to a Viesti inbound signal before emission. Rumus never reads Bija directly at runtime.

3.10 No Loops

Rumus contains no loop syntax. Every function is provably finite. Iteration is expressed as an explicit cycle in the Viesti signal graph, with Bija entities holding intermediate state between steps.

3.11 Memory Contract

Rumus functions operate exclusively in registers. No heap allocation occurs inside a Rumus function. When a function returns, every intermediate value is gone. The only way a result persists is if Viesti writes an output address to a Bija entity. Garbage collection is an inapplicable concept — there is nothing to collect.

3.12 Full Rumus Example

@Rumus
@rumus-riscv

? hitCheck{roll, defense};
  threshold: defense - 5
  hit: roll >= threshold ? 1 : 0
  ~hit

? critCheck{luck, roll};
  chance: 60 + luck
  crits: roll * 6 > chance ? 1 : 0
  ~crits

? strikeCalc{baseDmg, armor, isCrit};
  reduced: baseDmg - armor
  multiplied: isCrit = 1 ? reduced * 2 : reduced
  dmg: multiplied < 1 ? 1 : multiplied
  ~dmg

? takeDamage{health, dmg};
  result: health - dmg
  health > 0 -> result >= 0
  remaining: result < 0 ? 0 : result
  ~remaining

? transitionCombat{current, incoming};
  isIdle: current = idle
  isCombat: current = combat
  isDead: current = dead
  isDead !& isCombat
  isIdle -> incoming = combat
  isDead -> incoming = dead
  next: isDead ? current : incoming
  ~next

4. Viesti — The Signal Layer

4.1 Purpose

Viesti is the wiring of the application. It defines which data flows where. It does not compute. It does not store. It does not schedule. It routes.

Fudgel assumes a one-shot execution model. There is no built-in run loop, no frame tick, no event queue. Data exists in Bija, signals fire, functions run, results are written back to Bija. Recurring execution is a Lifga library concern. The core language has no concept of time.

A signal resolves when its source address has data available. This is a pull model. A function cannot execute until all of its parameter addresses have been populated by Viesti — no partial execution, no error, the condition is simply not yet met. Signal order is execution order.

4.2 Sequential Signals — ~

source ~ function ~ target
PartDescription
sourceNamed address on a Bija entity, or a parameter address on a function entity
functionA Rumus function entity
targetNamed address on a Bija entity, or a parameter address on a function entity

4.3 Parallel Signals — ~~

Hero.luck ~ hitCheck.roll ~ Hero.luck     (sequential seed)

Hero.luck ~~ critCheck.luck               (parallel pool opens here)
  Battle.round ~ critCheck.roll
  Hero.attack ~~ strikeCalc.baseDmg
  Config.armorBase ~ strikeCalc.armor
  critCheck ~ strikeCalc.isCrit
  strikeCalc ~ takeDamage.dmg

Boss.health ~ takeDamage.health           (implicit barrier — all pools resolved)

The double tilde ~~ opens a parallel thread pool at the current execution depth. Signals declared under a ~~ are dispatched simultaneously. The main thread waits for all pools at the current depth to fully resolve before proceeding to the next sequential signal. The barrier is implicit — defined by indentation, not by any keyword. Nesting works recursively; the tree resolves leaves-first.

4.4 Inline Parallel Chains

A ~~ B ~~ C                          (A, B, C dispatched in parallel)

A ~~ B ~~ C ~ D ~ E ~~ F            (barrier at ~; D then E sequential; new pool at F)
Race condition warning: in A ~~ B ~~ C, B is simultaneously being written to and read from with no ordering guarantee. ~~ chaining is only safe when no address appears on both sides of adjacent ~~ operators. The IDE will flag overlapping addresses.

4.5 Parallelism Verification — @Peril

The Peril library is a compilation pass that verifies the independence claims of every ~~ pool. For each pool, Peril runs the pool's signals in both possible orderings against the full expanded Bija state, then compares outputs. Matching outputs prove independence. Differing outputs mean a dependency exists that was not declared — compilation halts with the conflicting signal path.

This is not a runtime check. It is a compiler-level correctness proof. @Peril is optional. If a program produces different outputs on two runs with identical input, there is exactly one cause in Fudgel: a ~~ where an address is being written and read without ordering.

4.6 Dormant Signals

Player.health ~ checkCritical ~

A signal with no output target is dormant. Absent from the execution graph entirely — a compile-time known absence, not a runtime invalid state. Visible in the IDE as an unwired edge. Wiring an output activates it immediately without any other change.

4.7 Iteration via Signal Cycles

WorkQueue.current ~ processItem ~ WorkQueue.result
WorkQueue.result ~ checkDone ~ WorkQueue.isDone
WorkQueue.isDone ~ onFalse ~ WorkQueue.current
WorkQueue.isDone ~ onTrue ~ Output.final

Iteration is expressed as an explicit cycle in the Viesti graph. A signal targets a Bija entity that is itself the source of another signal, creating a feedback path. The cycle is fully visible in the graph. There is no hidden looping.

4.8 Conditional Routing

Viesti has no conditional syntax. A signal resolves unconditionally when its source has data. Conditional branching is the result of a Rumus function writing to distinct Bija addresses. Separate signals are wired from each. The decision was made inside Rumus.

4.9 Full Viesti Example

@Viesti

Hero.luck ~ hitCheck.roll ~ Hero.luck

Hero.luck ~~ critCheck.luck
  Battle.round ~ critCheck.roll
  Hero.attack ~~ strikeCalc.baseDmg
  Config.armorBase ~ strikeCalc.armor
  critCheck ~ strikeCalc.isCrit
  Boss.health ~~ takeDamage.health
  strikeCalc ~ takeDamage.dmg

takeDamage ~ Boss.health

Boss.health ~ phaseCheck.health
Config.maxHealth ~ phaseCheck.maxHp
phaseCheck ~ Boss.phase

(dormant — xp formula not yet wired)
Battle.lastDmg ~ xpGain.dmg
Battle.round ~ xpGain.round
xpGain ~

5. Layer Agnosticism

The most important architectural principle in Fudgel is not the Trinity itself — it is that each layer is completely blind to the existence of the others.

LayerKnows only how to…
BijaParse entities, properties, and boolean sub-entities. Store them. Display them as a tree.
RumusAccept named inputs, evaluate a sequence of expressions, write results to ~ output addresses, and return. It does not know where inputs came from or where outputs are going.
ViestiRead a source address, invoke a function, write to a target address. It does not know what the function does, what types mean, or when anything should happen.
LibrariesOperate on the Bija state passed to them. They do not know other libraries exist.

This is not a design preference. It is what makes the system provably simple. Agnosticism means every layer can be understood, tested, and replaced in complete isolation.

5.1 The Library Compilation Pass

Because Bija stores all metadata booleans as plain sub-entities, libraries can participate in compilation by reading that state and acting on it before the PAF is emitted. This is how type checking, validation, and constraint enforcement work without any of those concepts existing in the core language.

The compilation pipeline is ordered:

  1. Bija parsing — produces a flat entity tree. No semantics assigned.
  2. Library passes — each participating library runs its Rumus functions over the entity tree in import declaration order. Any pass that returns false halts compilation and surfaces the failing entity path.
  3. PAF emission — the validated entity tree is compiled to binary format. Names are discarded. Addresses are assigned.

6. The Memory Model

LayerMemory role and lifetime
BijaThe entire application heap. Entities are allocated at load time and persist until the application terminates or a signal writes empty to them. No dynamic allocation occurs at runtime.
RumusRegisters only. Allocated at function entry, freed at function return. Nothing escapes unless Viesti explicitly writes the output to a Bija entity. Garbage collection is an inapplicable concept.
ViestiThe write path between Rumus and Bija. Not a memory region. A signal is a resolution operation, not a data store.
Invariant: a value exists in one of two states — stored in a named Bija entity, or in transit through a Rumus function. It cannot be in both simultaneously. It cannot be anywhere else.

7. The Domain Libraries

The Fudgel standard library is composed of domain-specific libraries. Each library is agnostic: it does not know other libraries exist. Libraries are domain-scoped semantic abstractions that compile directly to Bija entities, Rumus functions, and Viesti signals. There is no distinction at the machine level between library code and author code.

7.1 The @ Declaration

@ has one universal meaning: bring this into scope. The Trinity is not privileged at the syntax level — only at the architecture level. A library file is a Fudgel source file. There is no intermediate compiled format. The source is the library.

@Ehto              (standard library)
@path/to/library   (custom or third-party)
@../shared/myUtils

7.2 Libraries Are Inherently Open

Fudgel has no proprietary library model. This is not a licensing stance — it is a structural consequence. There is no intermediate compiled format, no opaque binary, no bytecode to distribute in place of source. The conventional proprietary library model is architecturally impossible in Fudgel.

7.3 Standard Libraries

Target Libraries

LibraryDescription
@rumus-riscvNumeric arithmetic and comparison primitives for RISC-V execution. Current working target.
@rumus-wgslPlanned. Shader operation primitives for GPU execution via WGSL.
@rumus-llvmPlanned. LLVM IR emission for broad hardware coverage.

Domain Libraries

LibraryMeaningResponsibility
TipusTypeUser input handling
EhtoConstraintType checking and validation
PrimiFirstMemory management
PerilPerilParallelism verification — proves ~~ independence at compile time
LifgaReviveTiming, sequences, animation — the only library that introduces time
AquillaEagleVisibility and framebuffer rendering
KadroFrameLayout and structural templates
SminkMakeupStyling, formatting, and theming
RezoNetworkHigh-performance network handling
ZapisRecordFile system and database I/O
NësenNestDynamic entity allocation — operates in a separate memory pool
GiyaGuidanceAccessibility and semantic markers
HloovChangeTranslation and localisation
KaniSoundAudio processing and playback
PismoFontText formatting and typography
CeadPermissionUser access control and security
NyemSqueezeCompression and storage
DrysuTangledEncryption and cryptographic safety

7.4 The Visual Library Stack

Kadro, Smink, and Aquilla are not graphics libraries in the traditional sense — they are semantic description layers that happen to produce visual output.

  • Kadro answers where is this and how big — pure spatial arithmetic, entirely CPU-expressible.
  • Smink answers what does this look like — property resolution over the entity tree, pure Rumus computation.
  • Aquilla answers what is visible and in what order — a visibility and compositing pass over what Kadro and Smink already resolved.

The rendering model is invalidation-based, not loop-based. A signal fires, Aquilla resolves the affected region, pixels update, execution stops. A Fudgel application with no state changes consumes essentially zero CPU — genuinely idle until a signal fires. There is no virtual DOM reconciliation, no full frame diff. The diff is free because it is the signal table.

7.5 Physics and Simulation

Fudgel has no frame budget. A physics signal fires, the simulation runs Rumus functions to completion — however many iterations convergence requires — and writes the result to Bija. The physics can be as accurate as the algorithm, not as accurate as the budget allows.

Precalculation and baking are first-class citizens. Because Bija is fully declared state, the boundary between precalculated and live is explicit and visible in source. A developer can run the full simulation at authoring time, write the results into Bija entity states, and at runtime apply only the delta between the baked path and actual player interaction.

8. The PAF — Published Application Format

The PAF is the compiled, binary-efficient output of a finalised Fudgel application. It is a shipping format, not a development format.

8.1 Memory Model

PAF entity blocks are variable width — the PAF header maps entity addresses to byte offsets. Lookup is O(1) via the index rather than O(n) traversal. The PAF declares its total memory footprint upfront in the header. The runtime allocates this once on load. There is no allocator at runtime — the compiler made every allocation decision.

8.2 Header Structure

[total_memory_footprint]   — runtime allocates this once
[entity_count]             — length of the index
[entity_start_offsets]     — flat array, one value per entity
[signal_count]             — length of signal table
[signal_table]             — flat array of address pairs
[data_region_offset]       — where bulk bytestreams begin

8.3 PAF Space and Nësen Space

PAF space is entirely static, entirely compiler-decided, entirely predictable. A program that does not import Nësen has provably no dynamic allocation, no GC, no runtime memory management of any kind — an auditable guarantee, not a claim.

Nësen operates in a separate memory pool outside PAF space with its own allocator. PAF addresses and Nësen addresses occupy distinct ranges. A signal destination is unambiguously one or the other from the address value alone — no type tag, no runtime check.

8.4 Format Properties

A PAF file contains no source names, no type information beyond memory layout requirements, and no runtime interpreter. It is loaded by the Fudgel Runtime, which maps it into memory and begins executing from the first signal declared in the application's @Viesti section. Distributing a PAF is publishing an application. The PAF format specification is a separate document currently under development.

9. The Fudgel IDE

9.1 Architecture

The Fudgel IDE is a multitab environment. The primary tab presents the application as a live schema — a node graph of the Bija entity tree and Viesti signal graph that actually runs. It is not a mockup. It is the application, rendered as a readable structure, executing in real time or at controllable speed.

The graph view is strictly read-only. The canonical source of truth is always the code the developer writes. By keeping the graph read-only, Fudgel retains full compatibility with version control, code review, and the entire text-based tooling ecosystem. This is only possible because Fudgel's source structure and execution structure are the same thing — there is no translation to visualise.

9.2 Library Tabs

Each domain library gets its own IDE tab. The render library tab shows the visual layout. The signal library tab shows timing and event flow. The type library tab shows constraint validation state across the entity tree. This segments complexity by domain — nobody has to look at the full graph at once unless the task requires it.

9.3 Code-to-Graph Navigation

Selecting a node in the graph view jumps to the Bija entity or Viesti signal that defines it. Editing the source updates the graph immediately.

9.4 MVP Visualiser

The full IDE is gated on library maturity. The immediate priority is an MVP visualiser that parses Fudgel source and renders the signal graph, showing data movement in real time or at controlled speed. This validates the core Trinity implementation and makes the language comprehensible to people who have not read the spec.

10. Build Order

StageDeliverable and rationale
1. Core compilerParser for Bija, Rumus emitter targeting RISC-V, Viesti graph builder including parallel pool resolution. The minimum needed to produce a valid PAF from source.
2. MVP visualiserRead-only graph renderer showing the live Bija entity tree, Viesti signal flow, and parallel execution clusters. Makes the language legible to contributors.
3. Base librariesEhto (type/constraint), Primi (memory), Lifga (timing), and a base iteration library. Enables real programs to be written.
4. Domain librariesAquilla, Kadro, Smink, and the remaining domain-specific libraries, developed in order of need.
5. Full IDEMultitab environment with domain library tabs, built reactively from what each library proved it needs to show.

11. Architectural Invariants

The following invariants must hold in any valid Fudgel application. They are not conventions. A Fudgel compiler must reject any application that violates them.

  • Every Fudgel file must declare its Trinity imports explicitly. A file's @ block is its interface declaration.
  • @ has one universal meaning: bring this into scope. The Trinity DSLs use the same syntax as domain libraries.
  • A file's type is defined by its imports. An application declares all three Trinity layers. A library declares @Bija and @Rumus but not @Viesti. A data file declares only @Bija.
  • The first signal declared under @Viesti is the application entry point. Execution begins there and proceeds in signal declaration order.
  • Bija contains no unresolved references. Every address is fully determined at load time.
  • Bija values are always atomic. A value is a literal, a reference, a shorthand token, or empty. Never an expression.
  • The * list marker creates named sub-entities with empty values. Names are the data. The - list marker creates numbered sub-entities with their values. Neither construct exists in the compiled output.
  • The ! multiplier resolves at parse time to N independent named instances. The count must be a literal integer.
  • A Fudgel program is deterministic by construction. Non-determinism can only be introduced explicitly through a library.
  • Bija has no privileged keywords. All metadata booleans are plain sub-entities. Their meaning belongs to the library that reads them.
  • Rumus function parameters are declared using {} boolean shorthand. They expand to sub-entities with value true. Viesti overwrites them before execution. A function cannot run unless all parameters have been supplied.
  • The only externally addressable values on a Rumus function entity are its parameter addresses and its ~ declared output values. All body properties are ephemeral and do not exist in the PAF.
  • Body property names in Rumus are discarded at emit time and become positional register slots. Overflow is undefined behaviour.
  • A Rumus function called by a single caller is inlined at emit time. A function called by multiple callers is emitted as a subroutine. This is a determinism guarantee, not an optimisation.
  • Rumus never reads Bija directly at runtime. # in Rumus is always expanded to a Viesti signal at compile time.
  • Viesti signals resolve in the order they are written in the file. Signal order is execution order.
  • A signal resolves when its source address has data available. This is a pull model. A function does not fire until all parameters are populated.
  • Sequential signals (~) resolve completely before the next signal begins.
  • Parallel signals (~~) are dispatched simultaneously to available threads. The main thread waits for all pools at the current depth before the next sequential signal. The barrier is implicit, defined by indentation.
  • ~~ chaining is only safe when no address appears on both sides of adjacent ~~ operators. Overlapping addresses produce a race condition.
  • A signal with no output target is dormant. It is a compile-time known absence, not a runtime invalid state.
  • Timing, scheduling, animation, and recurring execution are exclusively Lifga library concerns. Viesti has no concept of time.
  • Every Rumus function is finite. No function may directly recurse or contain cycles in its call graph.
  • Iteration exists only as explicit cycles in the Viesti signal graph.
  • The only mechanism that writes to Bija at runtime is a Viesti signal carrying a function output value.
  • No layer knows another layer exists. Bija does not know Rumus exists. Rumus does not know Viesti exists. Libraries do not know other libraries exist.
  • Library compilation passes run in import declaration order. Import order is pass order.
  • There is no compiler optimisation pass. Performance is a product of design quality.
  • Rumus core is substrate-independent. Target libraries declare substrate-specific instruction mappings. The current working target is @rumus-riscv.
  • The Rumus standard operation set covers five domains: arithmetic (+ - * / % and unary -), comparison (= != < > <= >=), boolean (& | !), conditional (a ? b : c), and formal logic (-> <-> ^ !& !| <-). Bitwise operations are hardware-specific and belong in a systems target library.
  • Formal logic operators are assertions, not expressions. If an assertion fails, execution halts with the function path and the specific assertion that failed.
  • The conditional a ? b : c produces a value through pure transformation of inputs. Any selection that routes to meaningfully different behaviours belongs in Viesti.
  • File I/O, database access, and session persistence are exclusively Zapis library concerns.

Appendix — Quick-Reference Card

Bija Symbols

TokenMeaning
EntityName;Entity declaration
name: valueProperty
name: emptyExplicit empty value
#Entity.propAlias (same address, resolved at compile time)
=Entity.propValue copy (copied at parse time, source forgotten)
| v1, v2 |Positional override
!NMultiplier — stamp N instances
-Ordered list marker
*Unordered list marker
{flag}Metadata boolean
[...]Literal string (no parsing inside, may span lines)
(...)Comment

Rumus Symbols

TokenMeaning
? Name{p1, p2};Function declaration
name: exprNamed binding (let)
~nameOutput declaration
#Entity.propLive Bija read (compiler expands to inbound signal)
+ - * / %Arithmetic
-xUnary negation
= != < > <= >=Comparison (= is equality, not assignment)
& | !Boolean AND / OR / NOT
a ? b : cConditional
a -> bImplication assertion
a <-> bBiconditional assertion
a ^ bExclusive or assertion
a !& bNAND assertion
a !| bNOR assertion
a <- bConverse implication assertion

Viesti Symbols

TokenMeaning
A ~ B ~ CSequential signal chain
A ~~ BParallel signal (concurrent with peer ~~ signals)
A ~ B ~Dormant signal (no target)