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.
The Primitive
Everything in New Code is built on one type: the waveform number 𝕎. A waveform number has 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.
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.
Think of the three clauses as three gestures of the conductor's baton:
| Clause | Gesture | What it says |
|---|---|---|
| intent | bringing the orchestra in | what the function should do |
| forbid | the cut-off, the don't-do-that | what the function must not do |
| ensure | the sustain, the hold | what must be true at the end |
Writing good intents
- Name the primitive. The compiler knows the operator names. "Make it more similar" is ambiguous; "increase the coherence with e₀" is not.
- Be literal about forbid clauses.
「do not modify phase」means φ may not change. - Use ensure for invariants. Anything you want true of the output — a bound, a shape, a sign — put it in ensure.
- 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.
The Operators
These are all the operations defined on 𝕎. You can call them by symbol or by function name — the expression grammar accepts both.
| Symbol | Function | What 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. |
| ⌊·⌉_amp | collapse_amp(a) | Collapse by amplitude only. |
| ⌊·⌉_rms | collapse_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.
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:
| Symbol | Name | Meaning |
|---|---|---|
| ▶ | Series | The output of each process feeds the input of the next: audio_in ▶ classify ▶ result |
| ∥ | Parallel | Two processes run independently, yielding a pair: left_channel ∥ right_channel |
| ↺ | Feedback | A 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.
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 mirrorpitch_follow— a perfect fifth aboveamplitude_mirror— same amplitude on both sidesphase_opposition— phase shifted by π
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.
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.
Modes
In v1.0 the compiler has three explicit modes. The mode is the only thing that decides what happens to an intent hole (≔ ??).
| Mode | What happens to ≔ ?? | Network |
|---|---|---|
| offline | Offline pattern matcher; raises OfflineHoleError if no match | No |
| online | Calls Anthropic API; AST safety pass; writes to snapshot store | Yes |
| snapshot | Replays a SnapshotStore from disk; miss falls through | No |
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.
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
| Command | Effect |
|---|---|
| :provenance amplify | Show the full provenance record |
| :src amplify | Show the compiled body |
| :accept amplify | Persist the body to the snapshot store |
| :reject amplify | Remove 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.
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.
The Workflow
When you sit down to write a New Code score, the order goes:
- Decide what you are modelling. An audio signal? A belief? A UI state? A transaction? The type is the same.
- Write the literals.
letbindings for the 𝕎s you already know. - Write the function signatures with intents. Header, intent/forbid/ensure,
≔ ??. - Pick a mode.
--offlinefor known patterns;--onlineto let the model fill new holes;--snapshotonce you have a pinned recording. - Compile in the REPL. Read the provenance, read the body, run the function, watch the debugger.
:acceptthe bodies you trust. Now the score compiles without the network. Commit the snapshot file.- Watch processes unfold.
:debugand run them in the browser. - 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.
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
| Group | Names |
|---|---|
| Primitive | W · w · Shape · sine · sq · tri · saw · pulse · e0 · silence |
| Operators | superpose · oscillate · invert · drift · coherence · d_gamma |
| Collapse | collapse · collapse_amp · collapse_freq · collapse_rms |
| Diagnostics | fingerprint · identifiability_horizon |
| Processes | Process · every · on · while_ · series · parallel · feedback · unfold · drift_process |
| Coupling | Entangled · entangle · pitch_follow · amplitude_mirror · phase_opposition · identity |
| Math | pi · tau_const · e_const · sqrt · exp · log · sin · cos · tan |
| Functional | fold_left · fold_right · take · drop · compose · pipe · head · tail |
Mode cheatsheet
| You wrote | Offline | Online | Snapshot |
|---|---|---|---|
≔ ?? known pattern | Offline backend matches | API call (cached if present) | Replay if present |
≔ ?? unknown pattern | OfflineHoleError | API call, AST-checked, cached | Falls through to fallback |
≔ <expr> | Parse + lower | Parse + lower | Parse + lower |
extern python { } | Runs | Runs | Runs |