← Documentation / DOC-01 · GUIDE · v1.0

The Conductor's
Guide to New Code

This is the document you read when you sit down to write. Not theory — the twelve chapters that take you from a blank file to a running score. The conductor writes the score; the AI compiler is the orchestra.

The shape of the thing

New Code is written like an orchestral score. You describe, in a dedicated kind of sentence called an intent, what the music should do. The AI compiler plays it.

You do not write loops. You do not write control flow by hand. You write:

  • what the function should accomplish (intent)
  • what it must never do (forbid)
  • what must be true at the end (ensure)

And then, where a conventional language would have you write the body, you write ??. The compiler fills it in. You read the result the way a doctor reads blood work — through diagnostics, through the debugger, through tests — not by following the code line by line.

v1.0 key feature: When you accept a body the compiler produced (:accept), it is persisted to a snapshot store keyed by intent. The next compile replays it without an API call — deterministic, pinnable under version control.

/ Chapter 01

The Primitive

Everything in New Code is built on one type: the waveform number 𝕎. A waveform number has four components:

f FREQUENCY , A AMPLITUDE , φ PHASE , σ SHAPE EXAMPLE ⟨ 440, 1.0, 0, sine ⟩ concert A · 440 Hz — 𝕎 · the fundamental value type · four components
  • f — how often the value updates or repeats (cycles per unit time)
  • A — the amplitude, or magnitude
  • φ — the phase, or offset within the cycle (radians)
  • σ — the shape, a unit-periodic function: sine, sq, tri, saw, pulse(duty)

The name is "waveform" because audio is the domain where all four components are already native vocabulary. But the abstraction is general. A UI state has all four: how often it updates, how loud the change is, where in the user's attention it arrives, and what curve it takes getting there.

Your first score

Open a file, call it first.nc, and write:

※ A 440 Hz tone.
let concert_a : 𝕎 = w(f=440, A=1.0, phi=0.0, sigma=sine)

Load it in the REPL:

python -m newcode.repl first.nc

loading first.nc...
  let concert_a = ⟨440, 1, 0, sine⟩
New Code v1.0 — mode: offline
nc> concert_a.realise(0.25)
0.9999952938095761

realise(t) evaluates the waveform at a particular time. This is how 𝕎 becomes a number. The descriptor itself does not change; what changes over τ is the scalar you observe through realise.

/ Chapter 02

Intent Blocks

You do not write the body of a function by hand. You write three clauses that describe it to the compiler, then a ?? where the body would go.

INTENT BLOCK · STRUCTURE fn amplify (signal : 𝕎, gain : ℝ) → 𝕎 intent: 「scale amplitude while preserving shape」 WHAT forbid: 「do not modify phase」 NOT ensure: 「output shape equals input shape」 TRUE ?? ← compiler fills this — the three baton gestures · intent · forbid · ensure

Think of the three clauses as three gestures of the conductor's baton:

ClauseGestureWhat it says
intentbringing the orchestra inwhat the function should do
forbidthe cut-off, the don't-do-thatwhat the function must not do
ensurethe sustain, the holdwhat must be true at the end

Writing good intents

  1. Name the primitive. The compiler knows the operator names. "Make it more similar" is ambiguous; "increase the coherence with e₀" is not.
  2. Be literal about forbid clauses. 「do not modify phase」 means φ may not change.
  3. Use ensure for invariants. Anything you want true of the output — a bound, a shape, a sign — put it in ensure.
  4. One intent per function. Two intents joined by "and" → split the function.

Intent text is part of the cache key. Re-phrasing the intent re-prompts the model and creates a different snapshot. Keep intent strings stable once you're happy with the body.

/ Chapter 03

The Operators

These are all the operations defined on 𝕎. You can call them by symbol or by function name — the expression grammar accepts both.

SymbolFunctionWhat it does
superpose(a, b)Pointwise sum of two waveforms. Commutative, associative. This is the addition of New Code.
oscillate(a, b)Shape-composing multiplication — ring-modulates the shapes. Commutative, not associative.
invert(a)Shifts phase by π. Provides the additive inverse under superposition.
drift(a, δ)Ages a by δ toward the square-wave attractor. Shifts frequency, decays amplitude, rotates phase, deforms shape.
⟪·,·⟫coherence(a, b)Shape-sensitive correlation of two waveforms, in [−1, 1]. This is a collapse — it produces a scalar.
d_γd_gamma(a, b)Coherence distance, in [0, 1]. The metric on 𝕎 sensitive to shape, not just magnitude.
⌊·⌉collapse(a)Reduce a 𝕎 to a scalar. Information loss. Irreversible, explicit, marked in the type signature.
⌊·⌉_ampcollapse_amp(a)Collapse by amplitude only.
⌊·⌉_rmscollapse_rms(a)Root-mean-square over one period.

Drift is the important one. It is a single operator that captures ageing, decay, analysis, and thermodynamic termination. A program ends when it drifts to the square-wave attractor. Ending a process cleanly means letting drift complete — not aborting. Collapse is irreversible. Treat it like unsafe in Rust.

/ Chapter 04

Processes

A process is not a thread. It is an ongoing unfolding with its own internal clock, τ. There is no global clock — each process advances independently.

let beat : 𝕎 = w(f=1.0, A=1.0, sigma=sine)

nc> p = every(beat)
nc> unfold(p, 3)
[⟨1, 1, 0, sine⟩, ⟨1, 1, 0, sine⟩, ⟨1, 1, 0, sine⟩]

every(w) makes a process that emits w at each tick and advances its own τ. The descriptor does not mutate; what changes is realise(tau).

Process composition

Three ways to combine processes:

SymbolNameMeaning
SeriesThe output of each process feeds the input of the next: audio_in ▶ classify ▶ result
ParallelTwo processes run independently, yielding a pair: left_channel ∥ right_channel
FeedbackA process's output is fed back into its own input with a delay: reverb ↺ by 0.1s

Drift as termination

nc> aged = drift_process(every(concert_a), delta_per_step=0.2)
nc> unfold(aged, 3)
[⟨440, 1, 0, sine⟩,
 ⟨440, 0.819, …, sine→sq(0.20)⟩,
 ⟨440, 0.670, …, sine→sq(0.40)⟩]

Each step ages the waveform a little more. Amplitude decays. Shape bends toward square. This is what it looks like to end a process cleanly. You do not call stop(). You let the drift complete.

/ Chapter 05

Entanglement

Two values are entangled when there is a declared coupling function Φ such that any transformation of one induces Φ on the other. This is structural coupling promoted into the type system.

In conventional languages, "the toggle and its indicator should always agree" is a comment, maybe a test, maybe the thing that breaks in production. In New Code it is a type-level declaration enforced by the runtime.

let toggle   : 𝕎 = w(f=60, A=1.0, phi=0.0, sigma=tri)
let indicator: 𝕎 = w(f=60, A=1.0, phi=0.0, sigma=tri)

nc> pair = entangle(toggle, indicator, via=identity)
nc> pair.transform_left(invert)
⟨ ⟨60, 1, 3.14, tri⟩ ▷◁ ⟨60, 1, 3.14, tri⟩ ⟩ via identity

Entanglement is symmetric (both sides propagate) and not transitive (A ▷◁ B and B ▷◁ C does not imply A ▷◁ C). Non-transitivity is deliberate: coupling is declared between specific pairs, not inferred across chains.

Standard coupling functions

  • identity — exact mirror
  • pitch_follow — a perfect fifth above
  • amplitude_mirror — same amplitude on both sides
  • phase_opposition — phase shifted by π
/ Chapter 06

The Expression Grammar

In v1.0 there is a real expression layer. You write let-bindings, lambdas, blocks, conditionals, records, and lists in a Python/F#-flavoured surface.

Let-in

let scaled =
    let base = w(f=440, A=1.0, sigma=sine) in
    let gain = 1.5 in
    amplify(base, gain)

Lambdas

let double = \x -> x * 2
let mul    = \x y -> x * y

Blocks

let pipeline = {
    let a = every(concert_a)
    let b = drift_process(a, 0.05)
    unfold(b, 16)
}

If / then / else

let safe_gain = if gain > 10 then 10 else gain

The python escape hatch

let arr = python { [i * 2 for i in range(10)] }

When you genuinely need Python, wrap it in python { }. The forbidden-name list still applies — no eval, exec, or open.

/ Chapter 07

The Host Bridge

Two ways to reach Python from New Code. Both are explicit and auditable.

import host.<module>

import host.numpy as np

let arr = np.array([1, 2, 3, 4])

Top-level. Resolved through the typed HostBridge. Cached. Use this for consuming a Python library.

extern python { ... }

extern python {
    import numpy as np
    SAMPLE_RATE = 44_100
    def to_pcm(w):
        return np.sin(2 * np.pi * w.f * np.arange(SAMPLE_RATE) / SAMPLE_RATE)
}

Top-level only. The block runs once when the score loads, and its names become available everywhere in the score. The forbidden-name list does not apply — this is the deliberate trap door. Use this for setup: defining helpers, constants, configuring the host runtime.

/ Chapter 08

Modes

In v1.0 the compiler has three explicit modes. The mode is the only thing that decides what happens to an intent hole (≔ ??).

ModeWhat happens to ≔ ??Network
offlineOffline pattern matcher; raises OfflineHoleError if no matchNo
onlineCalls Anthropic API; AST safety pass; writes to snapshot storeYes
snapshotReplays a SnapshotStore from disk; miss falls throughNo

Explicit bodies (≔ <expression>) compile in any mode without an API call. The mode only matters for holes.

python -m newcode.repl --offline   first.nc
python -m newcode.repl --online    first.nc
python -m newcode.repl --snapshot=tests/fixtures/snap.json first.nc

Without a flag, the REPL picks based on NEWCODE_MODE then on ANTHROPIC_API_KEY. No key → offline.

/ Chapter 09

Provenance and the Approval Workflow

Every compiled artefact carries a Provenance record:

backend     : anthropic
model       : claude-sonnet-4-5
intent_hash : 7c9e3a2b4f8d1e6a
body_hash   : 9d4a7e2c1b3f6580
timestamp   : 2026-04-24T18:32:11+00:00
accepted    : False
notes       : 0 retries

The intent_hash is a stable 16-hex-char SHA-256 prefix over the declaration's shape. It does not depend on the body — same hole + same intent = same hash. The accepted field is the only user-controlled field.

The four REPL commands

CommandEffect
:provenance amplifyShow the full provenance record
:src amplifyShow the compiled body
:accept amplifyPersist the body to the snapshot store
:reject amplifyRemove it from the store

:accept is how you turn an exploratory online run into a deterministic replay. Once accepted, the same source compiles in snapshot mode without an API call. Commit the snapshot file to pin the body under version control.

/ Chapter 10

Watching It Unfold

You do not read a New Code program. You watch it unfold. Start the visual debugger from the REPL:

nc> :debug
debugger at http://127.0.0.1:54321/  (tracking 2 process(es), 1 entangled pair(s))

A browser tab opens. Every process in scope becomes a horizontal band: τ along the x-axis, the realisation plotted against y. Every entangled pair becomes its own row; the link between its left and right flashes when a transformation propagates.

Call unfold(p, 20) or pair.transform_left(invert) and watch the UI refresh in real time over Server-Sent Events. This is how you read a New Code program: by watching it play.

/ Chapter 11

The Workflow

When you sit down to write a New Code score, the order goes:

  1. Decide what you are modelling. An audio signal? A belief? A UI state? A transaction? The type is the same.
  2. Write the literals. let bindings for the 𝕎s you already know.
  3. Write the function signatures with intents. Header, intent/forbid/ensure, ≔ ??.
  4. Pick a mode. --offline for known patterns; --online to let the model fill new holes; --snapshot once you have a pinned recording.
  5. Compile in the REPL. Read the provenance, read the body, run the function, watch the debugger.
  6. :accept the bodies you trust. Now the score compiles without the network. Commit the snapshot file.
  7. Watch processes unfold. :debug and run them in the browser.
  8. When drift ends it, it has ended. Do not abort. Let the square-wave attractor be reached.

A good first session is fifteen minutes. A good first non-trivial score is a UI panel or a belief system — something that is not audio, to prove to yourself that the language is general.

/ Appendix

Quick Reference

Reserved vocabulary

Top-level: fn · let · process · module · import · extern

Clause: intent · forbid · ensure · requires · emits · consumes · guard

Expression: let in · if then else · and or not · match with · true false

Stdlib, one page

GroupNames
PrimitiveW · w · Shape · sine · sq · tri · saw · pulse · e0 · silence
Operatorssuperpose · oscillate · invert · drift · coherence · d_gamma
Collapsecollapse · collapse_amp · collapse_freq · collapse_rms
Diagnosticsfingerprint · identifiability_horizon
ProcessesProcess · every · on · while_ · series · parallel · feedback · unfold · drift_process
CouplingEntangled · entangle · pitch_follow · amplitude_mirror · phase_opposition · identity
Mathpi · tau_const · e_const · sqrt · exp · log · sin · cos · tan
Functionalfold_left · fold_right · take · drop · compose · pipe · head · tail

Mode cheatsheet

You wroteOfflineOnlineSnapshot
≔ ?? known patternOffline backend matchesAPI call (cached if present)Replay if present
≔ ?? unknown patternOfflineHoleErrorAPI call, AST-checked, cachedFalls through to fallback
≔ <expr>Parse + lowerParse + lowerParse + lower
extern python { }RunsRunsRuns