← Documentation / DOC-04 · SEMANTICS · v1.0 · RUNTIME LOCK

Semantics
Lock

This file fixes the runtime meaning of New Code v1.0 as it exists in this repository. When the spec, worked examples, and implementation drift, this file wins for v1.0.

§1 · 𝕎 is a Descriptor, Not a Self-Evolving Object

A value of type 𝕎 is a static description of a pattern of change. It has four fields:

FieldNameMeaning
fFrequencyHow often the described change repeats
AAmplitudeMagnitude of the change
phiPhaseOffset within the cycle (radians)
sigmaShapeA unit-periodic function σ : ℝ → [−1, 1]

𝕎 does not mutate or advance itself over time. What changes over time is the scalar obtained by observing it: w.realise(t) — which evaluates to A · σ(f·t + φ). The descriptor stays the same; the observation changes.

This means:

  • The descriptor may remain the same across process samples
  • The observed scalar can still differ from one τ to the next
  • The debugger visualises realise(tau), not the descriptor
※ w is a static descriptor
let concert_a : 𝕎 = w(f=440, A=1.0, phi=0.0, sigma=sine)

※ realise(t) returns the instantaneous observation
nc> concert_a.realise(0.0)
0.0
nc> concert_a.realise(0.25)
0.9999952938095761
nc> concert_a   ※ the descriptor itself is unchanged
⟨440, 1.0, 0.0, sine⟩

§2 · A Process Owns τ

A Process is the runtime object that unfolds over time. On every call to p.sample() the runtime:

  1. Evaluates the process step at the current τ
  2. Returns that value
  3. Advances τ by 1 / rate

τ is local to the process. There is no global clock. Two processes advance their own τ independently. When you need coordination, you must declare it explicitly through entanglement or synchronisation.

Process A τ = 0.00 → 0.02 → 0.04 → Process B τ = 0.00 → 0.05 → 0.10 → independent τ no global clock coordinate via: entanglement ▷◁ synchronisation ‖ — each process owns its own τ · coordination requires explicit declaration

§3 · every(w) Emits the Same Descriptor at Each Tick

every(w) does not mutate w. It creates a process that emits the same waveform descriptor each time it is sampled. The process advances; the descriptor does not. The observed scalar is obtained separately through realise(tau).

nc> p = every(concert_a)
nc> unfold(p, 3)
[⟨440, 1, 0, sine⟩,
 ⟨440, 1, 0, sine⟩,
 ⟨440, 1, 0, sine⟩]   ※ same descriptor, different τ at each step

The three descriptors are identical — what differs is the internal τ at which each was sampled. To observe the change, call realise(tau) on the emitted value.

§4 · Process Combinators Sample Child Processes

Combinators advance child processes through sample(), not by calling step(...) directly. Calling step(...) directly bypasses the child process clock and breaks the semantics.

series(a, b, ...)

  • Samples child processes left-to-right
  • Feeds each emitted value to the next child as incoming
  • If the next child does not accept an incoming argument, it ignores it
  • The final child's output is the output of the series

parallel(a, b)

  • Samples both children on each tick
  • Returns a pair of emitted values
  • Children advance independently — no synchronisation unless explicitly declared with

feedback(a, delay)

  • Feeds a's output back into its own input after delay seconds
  • The feedback loop is implemented with a ring buffer of length ceil(delay * rate)
  • The initial buffer contents are the zero-amplitude waveform

§5 · Compiler Modes

The compiler has exactly three modes. The mode is the only thing that decides what happens to an intent hole (≔ ??). Explicit bodies compile in any mode without an API call.

ModeHow selectedHole behaviourNetwork
offline--offline flag, or no API key presentTries offline pattern matcher. On miss: raises OfflineHoleError with instructions.No
online--online flag, with ANTHROPIC_API_KEY setCalls Anthropic API. Runs AST safety pass. Writes body to snapshot store.Yes
snapshot--snapshot=<path> flagLooks up intent_hash in the store. Hit: replays body. Miss: falls through to configured fallback.No

The compiler does not silently fall back between modes. If offline mode cannot resolve a hole, it tells you exactly what to do: add the intent to the offline pattern table, run --online, or write the body explicitly. No silent failure.

§6 · The Cache Key

The intent hash is a 16-hex-character SHA-256 prefix computed over the declaration's shape: name, kind (fn / process / let), argument types, return type, and the text of all three intent clauses (intent, forbid, ensure).

The body never participates in the cache key. Two functions with identical intent blocks but different bodies have the same intent hash. Changing the body of an explicit declaration does not trigger a recompile.

Changing any intent clause changes the hash. Re-phrasing the intent causes a cache miss and re-prompts the model. This is by design: intent text is specification, and changing the specification should require the compiler to re-derive the implementation.

※ intent_hash depends on this shape (name + types + clauses)
fn classify (sample : 𝕎, known : [signature]) → signature | fault<no_match>
    intent: 「identify the instrument…」
    forbid: 「return a match if distance exceeds 0.65」
    ensure: 「output is the unique nearest signature, or no-match」

※ intent_hash does NOT depend on the body
※ changing 「0.65」 to 「0.70」 WILL change the hash

§7 · The AST Safety Pass

Every body produced by the online compiler goes through an AST safety pass before being stored or used. The pass rejects bodies that contain:

  • Calls to eval, exec, __import__, or open
  • Access to __builtins__, globals(), or locals()
  • Any name in the forbidden-name list
  • Direct file system or network access outside declared consumes channels

A body that fails the AST pass is rejected with a diagnostic. The compiler retries with a corrective prompt (up to the configured retry limit, typically 3). If all retries fail, the hole remains unfilled and a compile error is raised.

§8 · The Approval Workflow

Every compiled artefact carries a Provenance record with six fields:

backend     : anthropic         ← offline | anthropic | snapshot | explicit
model       : claude-sonnet-4-5  ← model used for this compilation
intent_hash : 7c9e3a2b4f8d1e6a  ← stable over re-compilations with same intent
body_hash   : 9d4a7e2c1b3f6580  ← informational; not the cache key
timestamp   : 2026-04-24T18:32:11+00:00
accepted    : False              ← the only user-controlled field
— provenance record · every compiled artefact carries one

Workflow states

StateHow you get thereEffect
Compiled, not acceptedOnline compile completes. accepted: FalseBody is available in the REPL session. Not persisted for future runs.
AcceptedUser runs :accept amplifyBody written to snapshot store, keyed by intent_hash. Future runs in snapshot mode replay without API call.
RejectedUser runs :reject amplifyBody removed from snapshot store. Hole is open again.

:accept is the gate. It converts a one-off online run into a pinned, deterministic, version-controllable body. Commit the snapshot file to lock the behaviour. CI runs in snapshot mode and never touches the network.

§9 · Entanglement Runtime

An entangled pair is a runtime object wrapping two values and a coupling function Φ. The invariant is:

For any transformation T applied to one side, the other side is automatically transformed by Φ(T).

Implementation lock

  • Transformations go through the pair's transform_left / transform_right methods
  • Direct mutation of pair.left or pair.right bypasses the coupling and is a runtime error
  • Disentanglement (disentangle(pair)) returns the two independent values; the coupling function is discarded
  • Entanglement is not transitive — A ▷◁ B and B ▷◁ C does not create A ▷◁ C

§10 · Drift Semantics

The drift operator is defined componentwise on 𝕎. Given w = ⟨f, A, φ, σ⟩ and scalar δ:

ComponentAfter drift by δBehaviour at δ → ∞
Frequency ff + δ · A (amplitude-modulated shift)Increases monotonically
Amplitude AA · exp(−|δ|)Decays to 0
Phase φφ + δ · f (mod 2π)Rotates continuously
Shape σlerp(σ, sq, |δ|)Converges to square wave sq

The square wave sq is the attractor of the drift operator. It is the fixed point: sq ⇝ δ = sq for all δ. Thermodynamic termination occurs when a process reaches sq and amplitude has decayed to zero.

§11 · Forbidden Names

The following names are forbidden in compiler-generated bodies, in python { } escape blocks (but not in extern python { }), and in any expression evaluated through the REPL expression evaluator:

eval     exec      __import__   open
__builtins__   globals()   locals()
os.system   subprocess   socket
__class__   __base__   __subclasses__

Any use of a forbidden name in a compiler-generated body will cause the AST safety pass to reject the body. Any use in a python { } block will cause a compile error at the point of evaluation.

The extern python { } block is the deliberate exception — forbidden names are permitted there, because it is explicitly the "trust the human" escape hatch.