POP Schema README
A simulation.pop document is a semantic arrangement surface. It describes how a pop/ensemble simulation should unfold over time: what section is active, what guidance applies, what semantic pressures are present, and what runtime signals consumers should emit or observe.
The POP document is not intended to be a complete musical score, MIDI file, DAW session, or performance transcript. It is a compact declarative control surface for agents, renderers, adapters, simulations, and validators.
In plain terms: a POP spec tells a consumer what kind of moment it is, what pressures are active, and what the ensemble is allowed or encouraged to do.
Minimal identity
Every POP document must declare:
apiVersion: net.plantange/v1
kind: simulation.pop
ensemble: mister_sandman.pop
meta:
title: Mister Sandman
uuid: pop-sandman-adapter
apiVersion
The current schema expects:
apiVersion: net.plantange/v1
Consumers should reject or warn on unknown versions unless explicitly configured for compatibility.
kind
Must be:
kind: simulation.pop
This distinguishes POP specs from related documents such as plans, scores, regions, musicians, catalogs, or discovery records.
ensemble
A machine-readable ensemble identifier.
Examples:
ensemble: mister_sandman.pop
ensemble: circle_of_life.pop
ensemble: paradise.pop
ensemble: louie_louie.pop
The schema requires a lowercase, machine-friendly identifier matching:
^[a-z0-9_.-]+$
meta
Human-readable metadata.
meta:
title: Paradise by the Dashboard Light
uuid: pop-paradise-adapter
title is required. uuid is optional and may be null.
Mental model
A POP consumer usually performs this loop:
- Load and validate the POP document.
- Build a timeline from
sections. - At a given time, beat, bar, or frame, determine the current section.
- Apply matching
rules. - Merge applicable
arcmarker information. - Expose the resulting state as
pop.*runtime chems or guidance. - Allow actors, renderers, musicians, or simulations to respond.
- Optionally evaluate
metrics. - Emit declared
exports.
A useful runtime frame might look like:
{
"time_s": 35.0,
"bar": 20,
"section_id": "clarity",
"section_semantic": "clarity",
"pop": {
"suggested_pop_tension": 0.7,
"dream_suspension": "suspended",
"blend_authority": "dominant",
"charm_pressure": "held"
}
}
The document itself is the authored or generated source. Runtime agents should generally receive resolved POP structure as pop.* chems or guidance, not directly inspect or mutate the source document.
Timing fields
tempo: 140.0
steps_per_beat: 4
beats_per_bar: 4
tempo
Tempo in beats per minute. The schema describes this as informational and not necessarily used for frame-rate stepping. Consumers may still use it to convert between seconds, beats, bars, and musical output.
Recommended behavior:
- Use
tempowhen rendering music, MIDI, animation, or beat-aligned events. - Do not assume section boundaries are bar-aligned.
- Preserve original
start_sandend_svalues even when deriving beat positions.
steps_per_beat
The number of simulation or Jazz Form steps per POP beat.
Common value:
steps_per_beat: 4
This gives sixteenth-note style resolution in a 4/4 musical interpretation, but consumers should not assume music is the only use case.
beats_per_bar
Number of beats per bar.
Common value:
beats_per_bar: 4
Consumers that care about meter should use this field rather than hardcoding 4/4.
Source provenance
source:
kind: catalog_adapter
generator: sandman_arrangement_adapter
generator_version: null
discovery_ref: null
region_ref: null
musician_ref: null
seed: null
inputs_hash: null
The source block describes where the POP spec came from.
Allowed kind values:
authored
catalog_adapter
imported
generated
Recommended usage:
authored: hand-written spec.catalog_adapter: generated from a known catalog or adapter layer.imported: derived from external material.generated: algorithmically produced.
Consumers should treat this as provenance, not behavior. Behavior should come from sections, arc, axes, rules, metrics, field_actors, and substrate.
Substrate
substrate:
chord_loop:
- Bb
- G7
- Cm
- F7
key_root: Bb
loop_duration_beats: 8.0
tempo_feel: gentle_hypnotic
tonal_mode: doo_wop_major
substrate is an optional fake-book-like foundation. It may contain chords, keys, modes, breakdown markers, bridge chords, tempo feel, tonal mode, or any other consumer-specific foundation data.
The schema intentionally allows additional substrate properties.
Common fields seen in examples:
substrate:
chord_loop: [E, A, B]
bridge_chords: [A, E, B]
key_root: E
key: G_major
loop_duration_beats: 16.0
tempo_feel: anthemic_rock
tonal_mode: evans_major_vamp
breakdown_start_s: 305.0
breakdown_end_s: 370.0
runaway_tempo_start_s: 195.0
Recommended consumer behavior:
- Treat
substrateas optional. - Do not fail if it is
null. - Use known fields opportunistically.
- Preserve unknown fields when transforming documents.
- Prefer graceful degradation over rejection.
For musical renderers, substrate.chord_loop and substrate.loop_duration_beats can provide enough structure for simple accompaniment, bass movement, or seeded generative rendering.
Sections
Sections are the main timeline declarations.
sections:
- id: invitation
semantic: invitation
start_s: 0.0
end_s: 15.0
- id: deepening
semantic: deepening
start_s: 15.0
end_s: 35.0
Each section must have an id. It may also have:
semanticstart_send_sbar_startbar_end- additional consumer-specific fields
id
Stable section identifier. Metrics and arc markers may refer to this.
semantic
Semantic class or label for the section.
Rules often match against this:
rules:
- when:
section_semantic: clarity
set:
suggested_pop_tension: 0.7
start_s / end_s
Wall-clock section boundaries in seconds.
Consumers should validate that:
start_sis less than or equal toend_s.- Sections are ordered or can be sorted.
- Overlaps are either rejected or resolved intentionally.
- Gaps are either allowed, filled, or treated as no-section frames.
bar_start / bar_end
Optional bar-based section boundaries.
If both seconds and bars are present, consumers should document precedence. Recommended default:
- Use seconds when resolving wall-clock simulation.
- Use bars when rendering beat/bar-aligned musical material.
- Warn if seconds and bars imply meaningfully different boundaries.
Arc markers
Arc markers describe meaningful timed entrances, transitions, or narrative events.
arc:
- id: clarity_entry
section_id: clarity
start_s: 35.0
note: intent=ArriveAt;charm=Held;suspend=Suspended;blend=Dominant
Arc markers must have an id. They may include:
section_idstart_sbarnote- additional consumer-specific fields
Recommended interpretation
Use arc markers as semantic cues. They can be used to:
- mark section entrances,
- trigger arrangement changes,
- explain why a rule exists,
- provide human-readable intent,
- seed renderer decisions,
- generate debugging or visualization markers.
Structured notes
Some current examples encode structured data inside note strings:
note: hold=LoopUntilReleased;primitive=Disrupt;authority=AudiencePressure;budget=0.5
Consumers may optionally parse this form as semicolon-separated key/value pairs.
Recommended normalization:
CamelCase -> snake_case or lowercase categorical values
AllowAdvance -> allow_advance
AudiencePressure -> audience_pressure
LoopUntilReleased -> loop_until_released
However, structured data should ideally migrate toward explicit fields:
arc:
- id: baseball_broadcast_frenzy_entry
section_id: baseball_broadcast_frenzy
start_s: 195.0
guidance:
form_hold_policy: loop_until_released
opening_primitive: disrupt
authority_holder: audience_pressure
budget: 0.5
Until that migration happens, consumers should support notes as human-readable annotations and may support best-effort parsing.
Axes
Axes define semantic dimensions exposed by the POP surface.
axes:
- id: dream_suspension
value_kind: ordered_categorical
options:
- value: waking
rank: 0
- value: drifting
rank: 1
- value: suspended
rank: 3
default: waking
description: Depth of dream state.
Each axis requires:
id: dream_suspension
value_kind: ordered_categorical
Allowed value_kind values:
boolean
number
text
ordered_categorical
Ordered categorical axes
For ordered_categorical, provide ranked options:
options:
- value: forbidden
rank: 0
- value: emerging
rank: 1
- value: blended
rank: 2
- value: dominant
rank: 3
- value: unified
rank: 4
Ranks let consumers evaluate monotonicity, thresholds, intensity, or progression without knowing the full semantics of every value.
Recommended behavior:
- Validate that rule outputs match known options when practical.
- Allow equal ranks when two values are semantically different but equivalent in intensity.
- Do not assume rank always means “better” or “more desirable.” It means more advanced, more intense, or more authoritative according to the axis definition.
Axis defaults
Use default when no rule, arc marker, or actor contribution sets the value.
Consumer resolution order should be explicit. A reasonable default:
- Axis default.
- Section rule output.
- Arc marker guidance.
- Field actor contribution.
- Runtime override.
Different systems may prefer arc markers before rules. The important part is to document the order and make it deterministic.
Rules
Rules are selector-based guidance patches.
rules:
- when:
section_semantic: clarity
set:
blend_authority: dominant
dream_suspension: suspended
suggested_pop_tension: 0.7
Each rule has:
when: { ... }
set: { ... }
when
A selector object. The schema recognizes:
section_semantic: clarity
section_id: clarity
Additional selector keys are allowed.
Recommended rule matching:
- A rule fires when all
whenkeys match the current runtime context. section_idshould match the sectionid.section_semanticshould match the sectionsemantic.- Unknown selector keys should either be ignored with warning or matched against runtime context if present.
set
A guidance patch. Keys are open-ended.
Examples:
set:
suggested_pop_tension: 0.85
authority_holder: audience_pressure
form_hold_policy: loop_until_released
Common guidance fields:
suggested_pop_tension
intent
expression_restraint_hint
silence_policy
entrance_authority
form_hold_policy
authority_holder
opening_primitive
release_cause
transport_state
Consumers should not require every guidance key to be declared as an axis. Some fields are chems, hints, policies, or renderer-specific directives.
Suggested tension
suggested_pop_tension is a common numeric chem-like guidance field.
Recommended range:
0.0 to 1.0
Example interpretation:
0.0 fully relaxed / minimal pressure
0.3 light motion
0.5 active but controlled
0.7 high engagement
0.9 crisis / crown / forced commitment
1.0 maximum pressure
Renderers may map this to velocity, density, brightness, saturation, visual intensity, actor urgency, or probability of disruptive events.
Metrics
Metrics declare schema-level expectations.
metrics:
- id: vow_section_tension_high
description: suggested_pop_tension must be above 0.8 during vow_under_duress.
kind: chem_threshold
path: pop.suggested_pop_tension
op: gt
value: 0.8
when_section: vow_under_duress
Allowed metric kinds:
guidance_presence
chem_threshold
ordered_monotonic
custom
Metrics are not required for rendering, but they are valuable for validation and testing.
guidance_presence
Checks that a guidance field is present.
- id: entrance_authority_present
kind: guidance_presence
field: entrance_authority
With a section filter:
- id: instant_awe_silence_present
kind: guidance_presence
field: silence_policy
when_section: instant_awe
Consumers should evaluate this over resolved frames, not raw rules alone. A field may be supplied by defaults, arc guidance, actor contributions, or runtime overrides.
chem_threshold
Checks a numeric chem path.
- id: tension_high_at_clarity
kind: chem_threshold
path: pop.suggested_pop_tension
op: gt
value: 0.6
when_section: clarity
Supported operators:
lt
lte
eq
gte
gt
ordered_monotonic
Checks that an ordered axis rank does not decrease across specified sections.
- id: scale_pressure_monotonic
kind: ordered_monotonic
axis: scale_pressure
sections:
- instant_awe
- widening_grandeur
- communal_lift
- mythic_release
Consumers need an axis definition with ranked options and resolved values for each section.
custom
Use when validation requires domain-specific logic.
Consumers should report custom metrics as skipped unless they have a registered implementation.
Field actors
Field actors describe non-musician aggregate participants that shape runtime tension or guidance.
field_actors:
- id: chanters
description: Audience crowd acting as an aggregate chant instrument.
kind: aggregate_instrument
axes:
- id: participation_state
value_kind: ordered_categorical
options:
- value: unaware
rank: 0
- value: carrying
rank: 5
default: unaware
contributions:
- field: suggested_pop_tension
from_axis: participation_state
scale_by:
- mass
- alignment
Allowed actor kinds:
aggregate_instrument
custom
A field actor may define local axes. These do not automatically become top-level POP axes.
Contributions
A contribution declares how an actor affects a guidance field.
contributions:
- field: suggested_pop_tension
from_axis: participation_state
scale_by:
- mass
- alignment
Recommended interpretation:
- Normalize
from_axisrank to0.0..1.0. - Normalize each
scale_byaxis rank to0.0..1.0. - Multiply them together.
- Apply the result to the target field according to consumer policy.
Example:
participation_state = chanting_with -> 0.8
mass = dominant -> 0.75
alignment = supportive -> 1.0
contribution = 0.8 * 0.75 * 1.0 = 0.6
Consumers must define whether contributions add to, replace, clamp, or modulate an existing value.
Recommended default for tension-like fields:
resolved = clamp(base + contribution * contribution_weight, 0.0, 1.0)
Exports
exports:
arc_marker_signal: pop_arc_marker
guidance_signal: pop_guidance
exports declares runtime signal names.
Common exports:
arc_marker_signal
guidance_signal
Recommended behavior:
- If
exportsisnull, use consumer defaults. - If an expected export is missing, warn rather than fail unless the application requires it.
- Preserve unknown export names.
Example runtime events:
{
"signal": "pop_arc_marker",
"time_s": 105.0,
"section_id": "release",
"marker_id": "release_entry"
}
{
"signal": "pop_guidance",
"time_s": 105.0,
"section_id": "release",
"guidance": {
"blend_authority": "unified",
"dream_suspension": "woken",
"suggested_pop_tension": 0.25
}
}
Consumer design checklist
A robust POP consumer should support the following.
Loading
- Parse YAML or JSON.
- Support multi-document YAML streams when useful.
- Validate against the schema.
- Preserve unknown allowed fields where
additionalPropertiesis true. - Reject unknown top-level fields, because the top-level schema is closed.
Timeline resolution
- Sort sections by
start_sorbar_start. - Resolve current section for each frame.
- Handle missing
sectionsgracefully if allowed by application policy. - Decide how to handle gaps and overlaps.
- Convert seconds to beats and bars when needed.
Guidance resolution
- Start with axis defaults.
- Apply matching rules.
- Merge arc marker guidance if supported.
- Apply field actor contributions.
- Clamp numeric chems where appropriate.
- Normalize categorical values consistently.
Metrics
- Evaluate
guidance_presenceon resolved frames. - Evaluate
chem_thresholdon resolved chem paths. - Evaluate
ordered_monotonicusing axis ranks. - Report skipped custom metrics clearly.
Runtime output
- Emit guidance frames.
- Emit arc marker events.
- Optionally emit metric results.
- Optionally emit renderer-specific artifacts such as MIDI, animation cues, logs, or simulation traces.
Recommended frame representation
A consumer-independent frame structure might be:
{
"time_s": 90.0,
"beat": 144.0,
"bar": 36,
"step": 576,
"section_id": "fracture",
"section_semantic": "fracture",
"arc_markers": ["fracture_entry"],
"guidance": {
"entrance_authority": "lead_only",
"silence_policy": "required",
"suggested_pop_tension": 0.9
},
"pop": {
"entrance_authority": "lead_only",
"silence_policy": "required",
"suggested_pop_tension": 0.9
}
}
The guidance object is convenient for local renderer logic. The pop object mirrors the same values as chem-like runtime state.
Common patterns
Song as ritual emergence
sections:
- id: instant_awe
semantic: instant_awe
- id: widening_grandeur
semantic: widening_grandeur
- id: communal_lift
semantic: communal_lift
- id: mythic_release
semantic: mythic_release
This pattern uses monotonic scale or entrance axes. Good for gradual reveal, widening instrumentation, or staged permissions.
Song as negotiated transport
sections:
- id: seduction
semantic: seduction
- id: escalation
semantic: escalation
- id: baseball_broadcast_frenzy
semantic: baseball_broadcast_frenzy
- id: negotiation
semantic: negotiation
- id: vow_under_duress
semantic: vow_under_duress
- id: bitter_aftershock
semantic: bitter_aftershock
This pattern treats time advancement as governed by authority and hold policies.
Useful axes:
form_hold_policy
authority_holder
opening_primitive
Song as participation field
field_actors:
- id: chanters
kind: aggregate_instrument
This pattern lets the audience or environment become an actor.
Useful axes:
participation_state
chant_authority
ambiguity_pressure
Authoring guidance
Prefer explicit guidance over note parsing
Good:
rules:
- when:
section_semantic: clarity
set:
charm_pressure: held
dream_suspension: suspended
blend_authority: dominant
Less ideal:
arc:
- note: charm=Held;suspend=Suspended;blend=Dominant
Notes are useful, but executable guidance should be explicit where possible.
Keep casing machine-friendly
Prefer:
authority_holder: audience_pressure
form_hold_policy: loop_until_released
Avoid executable values like:
AudiencePressure
LoopUntilReleased
Human-facing notes may use prose or title case, but guidance values should match axis options.
Decide what is canonical
Avoid having arc.note and rules.set disagree.
If both describe the same state, define one as authoritative.
Recommended policy:
sectionsdefine time.rulesdefine executable guidance.arcdefines meaningful transitions and optional extra cues.metricsdefine validation expectations.
Declare axes for reusable categorical fields
If a field is used by many renderers or metrics, make it an axis.
For example, this should likely be an axis:
blend_authority: dominant
This may not need to be an axis:
release_cause: scale_alignment_reached
Metrics should test resolved behavior
A metric like this:
- kind: guidance_presence
field: entrance_authority
should pass if entrance_authority is supplied by any valid resolution layer, not only by direct rule assignment.
Validation levels
Consumers may implement multiple validation levels.
Level 1: Schema validation
Checks that the document matches the JSON Schema.
Examples:
- Required top-level fields exist.
apiVersionis correct.kindis correct.- Axis options have ranks.
- Rule objects have
whenandset.
Level 2: Referential validation
Checks references and internal consistency.
Examples:
- Every
arc.section_idexists insections. - Every
metric.when_sectionexists insections. - Every
ordered_monotonic.axisexists inaxes. - Every section has usable timing.
Level 3: Semantic validation
Checks whether resolved frames satisfy declared expectations.
Examples:
suggested_pop_tension > 0.8duringvow_under_duress.scale_pressurerank does not decrease.silence_policyis present duringinstant_awe.
Level 4: Consumer validation
Checks renderer- or simulation-specific requirements.
Examples:
- MIDI renderer requires
tempoand either achord_loopor fallback key. - Visualizer requires section timing.
- Transport simulator requires
form_hold_policy.
Example: minimal POP
apiVersion: net.plantange/v1
kind: simulation.pop
ensemble: tiny.pop
meta:
title: Tiny POP
uuid: tiny-pop-v1
tempo: 120.0
steps_per_beat: 4
beats_per_bar: 4
source:
kind: authored
generator: null
generator_version: null
discovery_ref: null
region_ref: null
musician_ref: null
seed: null
inputs_hash: null
substrate:
chord_loop: [C, F, G]
key_root: C
loop_duration_beats: 12.0
tempo_feel: simple
tonal_mode: major
sections:
- id: opening
semantic: opening
start_s: 0.0
end_s: 20.0
- id: lift
semantic: lift
start_s: 20.0
end_s: 40.0
arc:
- id: opening_entry
section_id: opening
start_s: 0.0
note: Establish the world.
- id: lift_entry
section_id: lift
start_s: 20.0
note: Increase density and brightness.
axes:
- id: scale_pressure
value_kind: ordered_categorical
options:
- value: opening
rank: 1
- value: lifted
rank: 2
default: opening
description: How large the field feels.
rules:
- when:
section_semantic: opening
set:
scale_pressure: opening
suggested_pop_tension: 0.3
- when:
section_semantic: lift
set:
scale_pressure: lifted
suggested_pop_tension: 0.7
metrics:
- id: lift_tension_present
kind: chem_threshold
path: pop.suggested_pop_tension
op: gt
value: 0.6
when_section: lift
field_actors: []
exports:
arc_marker_signal: pop_arc_marker
guidance_signal: pop_guidance
Compatibility notes
The top-level schema is closed with additionalProperties: false. Do not add new top-level fields casually.
The following areas are intentionally extensible:
substratesectionsitem objectsarcitem objects- rule
whenselectors - rule
setpatches exports
This creates a useful split:
- Top-level document shape stays stable.
- Domain-specific interpretation can evolve inside known extension pockets.
Practical consumer contract
A consumer does not need to understand every semantic field to be useful.
A minimal consumer only needs to:
- Load the document.
- Resolve current section by time.
- Apply matching rules.
- Emit guidance.
A richer consumer can additionally:
- parse arc notes,
- evaluate metrics,
- apply field actor contributions,
- render MIDI or visuals,
- simulate transport authority,
- generate logs and traces,
- expose
pop.*chems to agents.
The best consumers should be deterministic, tolerant of unknown extension fields, strict about schema identity, and explicit about resolution order.
That is the core bargain of POP: authored semantic pressure in, reproducible runtime behavior out.
POP Schema README
A simulation.pop document is a semantic arrangement surface. It describes how a pop/ensemble simulation should unfold over time: what section is active, what guidance applies, what semantic pressures are present, and what runtime signals consumers should emit or observe.
The POP document is not intended to be a complete musical score, MIDI file, DAW session, or performance transcript. It is a compact declarative control surface for agents, renderers, adapters, simulations, and validators.
In plain terms: a POP spec tells a consumer what kind of moment it is, what pressures are active, and what the ensemble is allowed or encouraged to do.
Minimal identity
Every POP document must declare:
apiVersion: net.plantange/v1
kind: simulation.pop
ensemble: mister_sandman.pop
meta:
title: Mister Sandman
uuid: pop-sandman-adapter
apiVersion
The current schema expects:
apiVersion: net.plantange/v1
Consumers should reject or warn on unknown versions unless explicitly configured for compatibility.
kind
Must be:
kind: simulation.pop
This distinguishes POP specs from related documents such as plans, scores, regions, musicians, catalogs, or discovery records.
ensemble
A machine-readable ensemble identifier.
Examples:
ensemble: mister_sandman.pop
ensemble: circle_of_life.pop
ensemble: paradise.pop
ensemble: louie_louie.pop
The schema requires a lowercase, machine-friendly identifier matching:
^[a-z0-9_.-]+$
meta
Human-readable metadata.
meta:
title: Paradise by the Dashboard Light
uuid: pop-paradise-adapter
title is required. uuid is optional and may be null.
Mental model
A POP consumer usually performs this loop:
- Load and validate the POP document.
- Build a timeline from
sections. - At a given time, beat, bar, or frame, determine the current section.
- Apply matching
rules. - Merge applicable
arcmarker information. - Expose the resulting state as
pop.*runtime chems or guidance. - Allow actors, renderers, musicians, or simulations to respond.
- Optionally evaluate
metrics. - Emit declared
exports.
A useful runtime frame might look like:
{
"time_s": 35.0,
"bar": 20,
"section_id": "clarity",
"section_semantic": "clarity",
"pop": {
"suggested_pop_tension": 0.7,
"dream_suspension": "suspended",
"blend_authority": "dominant",
"charm_pressure": "held"
}
}
The document itself is the authored or generated source. Runtime agents should generally receive resolved POP structure as pop.* chems or guidance, not directly inspect or mutate the source document.
Timing fields
tempo: 140.0
steps_per_beat: 4
beats_per_bar: 4
tempo
Tempo in beats per minute. The schema describes this as informational and not necessarily used for frame-rate stepping. Consumers may still use it to convert between seconds, beats, bars, and musical output.
Recommended behavior:
- Use
tempowhen rendering music, MIDI, animation, or beat-aligned events. - Do not assume section boundaries are bar-aligned.
- Preserve original
start_sandend_svalues even when deriving beat positions.
steps_per_beat
The number of simulation or Jazz Form steps per POP beat.
Common value:
steps_per_beat: 4
This gives sixteenth-note style resolution in a 4/4 musical interpretation, but consumers should not assume music is the only use case.
beats_per_bar
Number of beats per bar.
Common value:
beats_per_bar: 4
Consumers that care about meter should use this field rather than hardcoding 4/4.
Source provenance
source:
kind: catalog_adapter
generator: sandman_arrangement_adapter
generator_version: null
discovery_ref: null
region_ref: null
musician_ref: null
seed: null
inputs_hash: null
The source block describes where the POP spec came from.
Allowed kind values:
authored
catalog_adapter
imported
generated
Recommended usage:
authored: hand-written spec.catalog_adapter: generated from a known catalog or adapter layer.imported: derived from external material.generated: algorithmically produced.
Consumers should treat this as provenance, not behavior. Behavior should come from sections, arc, axes, rules, metrics, field_actors, and substrate.
Substrate
substrate:
chord_loop:
- Bb
- G7
- Cm
- F7
key_root: Bb
loop_duration_beats: 8.0
tempo_feel: gentle_hypnotic
tonal_mode: doo_wop_major
substrate is an optional fake-book-like foundation. It may contain chords, keys, modes, breakdown markers, bridge chords, tempo feel, tonal mode, or any other consumer-specific foundation data.
The schema intentionally allows additional substrate properties.
Common fields seen in examples:
substrate:
chord_loop: [E, A, B]
bridge_chords: [A, E, B]
key_root: E
key: G_major
loop_duration_beats: 16.0
tempo_feel: anthemic_rock
tonal_mode: evans_major_vamp
breakdown_start_s: 305.0
breakdown_end_s: 370.0
runaway_tempo_start_s: 195.0
Recommended consumer behavior:
- Treat
substrateas optional. - Do not fail if it is
null. - Use known fields opportunistically.
- Preserve unknown fields when transforming documents.
- Prefer graceful degradation over rejection.
For musical renderers, substrate.chord_loop and substrate.loop_duration_beats can provide enough structure for simple accompaniment, bass movement, or seeded generative rendering.
Sections
Sections are the main timeline declarations.
sections:
- id: invitation
semantic: invitation
start_s: 0.0
end_s: 15.0
- id: deepening
semantic: deepening
start_s: 15.0
end_s: 35.0
Each section must have an id. It may also have:
semanticstart_send_sbar_startbar_end- additional consumer-specific fields
id
Stable section identifier. Metrics and arc markers may refer to this.
semantic
Semantic class or label for the section.
Rules often match against this:
rules:
- when:
section_semantic: clarity
set:
suggested_pop_tension: 0.7
start_s / end_s
Wall-clock section boundaries in seconds.
Consumers should validate that:
start_sis less than or equal toend_s.- Sections are ordered or can be sorted.
- Overlaps are either rejected or resolved intentionally.
- Gaps are either allowed, filled, or treated as no-section frames.
bar_start / bar_end
Optional bar-based section boundaries.
If both seconds and bars are present, consumers should document precedence. Recommended default:
- Use seconds when resolving wall-clock simulation.
- Use bars when rendering beat/bar-aligned musical material.
- Warn if seconds and bars imply meaningfully different boundaries.
Arc markers
Arc markers describe meaningful timed entrances, transitions, or narrative events.
arc:
- id: clarity_entry
section_id: clarity
start_s: 35.0
note: intent=ArriveAt;charm=Held;suspend=Suspended;blend=Dominant
Arc markers must have an id. They may include:
section_idstart_sbarnote- additional consumer-specific fields
Recommended interpretation
Use arc markers as semantic cues. They can be used to:
- mark section entrances,
- trigger arrangement changes,
- explain why a rule exists,
- provide human-readable intent,
- seed renderer decisions,
- generate debugging or visualization markers.
Structured notes
Some current examples encode structured data inside note strings:
note: hold=LoopUntilReleased;primitive=Disrupt;authority=AudiencePressure;budget=0.5
Consumers may optionally parse this form as semicolon-separated key/value pairs.
Recommended normalization:
CamelCase -> snake_case or lowercase categorical values
AllowAdvance -> allow_advance
AudiencePressure -> audience_pressure
LoopUntilReleased -> loop_until_released
However, structured data should ideally migrate toward explicit fields:
arc:
- id: baseball_broadcast_frenzy_entry
section_id: baseball_broadcast_frenzy
start_s: 195.0
guidance:
form_hold_policy: loop_until_released
opening_primitive: disrupt
authority_holder: audience_pressure
budget: 0.5
Until that migration happens, consumers should support notes as human-readable annotations and may support best-effort parsing.
Axes
Axes define semantic dimensions exposed by the POP surface.
axes:
- id: dream_suspension
value_kind: ordered_categorical
options:
- value: waking
rank: 0
- value: drifting
rank: 1
- value: suspended
rank: 3
default: waking
description: Depth of dream state.
Each axis requires:
id: dream_suspension
value_kind: ordered_categorical
Allowed value_kind values:
boolean
number
text
ordered_categorical
Ordered categorical axes
For ordered_categorical, provide ranked options:
options:
- value: forbidden
rank: 0
- value: emerging
rank: 1
- value: blended
rank: 2
- value: dominant
rank: 3
- value: unified
rank: 4
Ranks let consumers evaluate monotonicity, thresholds, intensity, or progression without knowing the full semantics of every value.
Recommended behavior:
- Validate that rule outputs match known options when practical.
- Allow equal ranks when two values are semantically different but equivalent in intensity.
- Do not assume rank always means “better” or “more desirable.” It means more advanced, more intense, or more authoritative according to the axis definition.
Axis defaults
Use default when no rule, arc marker, or actor contribution sets the value.
Consumer resolution order should be explicit. A reasonable default:
- Axis default.
- Section rule output.
- Arc marker guidance.
- Field actor contribution.
- Runtime override.
Different systems may prefer arc markers before rules. The important part is to document the order and make it deterministic.
Rules
Rules are selector-based guidance patches.
rules:
- when:
section_semantic: clarity
set:
blend_authority: dominant
dream_suspension: suspended
suggested_pop_tension: 0.7
Each rule has:
when: { ... }
set: { ... }
when
A selector object. The schema recognizes:
section_semantic: clarity
section_id: clarity
Additional selector keys are allowed.
Recommended rule matching:
- A rule fires when all
whenkeys match the current runtime context. section_idshould match the sectionid.section_semanticshould match the sectionsemantic.- Unknown selector keys should either be ignored with warning or matched against runtime context if present.
set
A guidance patch. Keys are open-ended.
Examples:
set:
suggested_pop_tension: 0.85
authority_holder: audience_pressure
form_hold_policy: loop_until_released
Common guidance fields:
suggested_pop_tension
intent
expression_restraint_hint
silence_policy
entrance_authority
form_hold_policy
authority_holder
opening_primitive
release_cause
transport_state
Consumers should not require every guidance key to be declared as an axis. Some fields are chems, hints, policies, or renderer-specific directives.
Suggested tension
suggested_pop_tension is a common numeric chem-like guidance field.
Recommended range:
0.0 to 1.0
Example interpretation:
0.0 fully relaxed / minimal pressure
0.3 light motion
0.5 active but controlled
0.7 high engagement
0.9 crisis / crown / forced commitment
1.0 maximum pressure
Renderers may map this to velocity, density, brightness, saturation, visual intensity, actor urgency, or probability of disruptive events.
Metrics
Metrics declare schema-level expectations.
metrics:
- id: vow_section_tension_high
description: suggested_pop_tension must be above 0.8 during vow_under_duress.
kind: chem_threshold
path: pop.suggested_pop_tension
op: gt
value: 0.8
when_section: vow_under_duress
Allowed metric kinds:
guidance_presence
chem_threshold
ordered_monotonic
custom
Metrics are not required for rendering, but they are valuable for validation and testing.
guidance_presence
Checks that a guidance field is present.
- id: entrance_authority_present
kind: guidance_presence
field: entrance_authority
With a section filter:
- id: instant_awe_silence_present
kind: guidance_presence
field: silence_policy
when_section: instant_awe
Consumers should evaluate this over resolved frames, not raw rules alone. A field may be supplied by defaults, arc guidance, actor contributions, or runtime overrides.
chem_threshold
Checks a numeric chem path.
- id: tension_high_at_clarity
kind: chem_threshold
path: pop.suggested_pop_tension
op: gt
value: 0.6
when_section: clarity
Supported operators:
lt
lte
eq
gte
gt
ordered_monotonic
Checks that an ordered axis rank does not decrease across specified sections.
- id: scale_pressure_monotonic
kind: ordered_monotonic
axis: scale_pressure
sections:
- instant_awe
- widening_grandeur
- communal_lift
- mythic_release
Consumers need an axis definition with ranked options and resolved values for each section.
custom
Use when validation requires domain-specific logic.
Consumers should report custom metrics as skipped unless they have a registered implementation.
Field actors
Field actors describe non-musician aggregate participants that shape runtime tension or guidance.
field_actors:
- id: chanters
description: Audience crowd acting as an aggregate chant instrument.
kind: aggregate_instrument
axes:
- id: participation_state
value_kind: ordered_categorical
options:
- value: unaware
rank: 0
- value: carrying
rank: 5
default: unaware
contributions:
- field: suggested_pop_tension
from_axis: participation_state
scale_by:
- mass
- alignment
Allowed actor kinds:
aggregate_instrument
custom
A field actor may define local axes. These do not automatically become top-level POP axes.
Contributions
A contribution declares how an actor affects a guidance field.
contributions:
- field: suggested_pop_tension
from_axis: participation_state
scale_by:
- mass
- alignment
Recommended interpretation:
- Normalize
from_axisrank to0.0..1.0. - Normalize each
scale_byaxis rank to0.0..1.0. - Multiply them together.
- Apply the result to the target field according to consumer policy.
Example:
participation_state = chanting_with -> 0.8
mass = dominant -> 0.75
alignment = supportive -> 1.0
contribution = 0.8 * 0.75 * 1.0 = 0.6
Consumers must define whether contributions add to, replace, clamp, or modulate an existing value.
Recommended default for tension-like fields:
resolved = clamp(base + contribution * contribution_weight, 0.0, 1.0)
Exports
exports:
arc_marker_signal: pop_arc_marker
guidance_signal: pop_guidance
exports declares runtime signal names.
Common exports:
arc_marker_signal
guidance_signal
Recommended behavior:
- If
exportsisnull, use consumer defaults. - If an expected export is missing, warn rather than fail unless the application requires it.
- Preserve unknown export names.
Example runtime events:
{
"signal": "pop_arc_marker",
"time_s": 105.0,
"section_id": "release",
"marker_id": "release_entry"
}
{
"signal": "pop_guidance",
"time_s": 105.0,
"section_id": "release",
"guidance": {
"blend_authority": "unified",
"dream_suspension": "woken",
"suggested_pop_tension": 0.25
}
}
Consumer design checklist
A robust POP consumer should support the following.
Loading
- Parse YAML or JSON.
- Support multi-document YAML streams when useful.
- Validate against the schema.
- Preserve unknown allowed fields where
additionalPropertiesis true. - Reject unknown top-level fields, because the top-level schema is closed.
Timeline resolution
- Sort sections by
start_sorbar_start. - Resolve current section for each frame.
- Handle missing
sectionsgracefully if allowed by application policy. - Decide how to handle gaps and overlaps.
- Convert seconds to beats and bars when needed.
Guidance resolution
- Start with axis defaults.
- Apply matching rules.
- Merge arc marker guidance if supported.
- Apply field actor contributions.
- Clamp numeric chems where appropriate.
- Normalize categorical values consistently.
Metrics
- Evaluate
guidance_presenceon resolved frames. - Evaluate
chem_thresholdon resolved chem paths. - Evaluate
ordered_monotonicusing axis ranks. - Report skipped custom metrics clearly.
Runtime output
- Emit guidance frames.
- Emit arc marker events.
- Optionally emit metric results.
- Optionally emit renderer-specific artifacts such as MIDI, animation cues, logs, or simulation traces.
Recommended frame representation
A consumer-independent frame structure might be:
{
"time_s": 90.0,
"beat": 144.0,
"bar": 36,
"step": 576,
"section_id": "fracture",
"section_semantic": "fracture",
"arc_markers": ["fracture_entry"],
"guidance": {
"entrance_authority": "lead_only",
"silence_policy": "required",
"suggested_pop_tension": 0.9
},
"pop": {
"entrance_authority": "lead_only",
"silence_policy": "required",
"suggested_pop_tension": 0.9
}
}
The guidance object is convenient for local renderer logic. The pop object mirrors the same values as chem-like runtime state.
Common patterns
Song as ritual emergence
sections:
- id: instant_awe
semantic: instant_awe
- id: widening_grandeur
semantic: widening_grandeur
- id: communal_lift
semantic: communal_lift
- id: mythic_release
semantic: mythic_release
This pattern uses monotonic scale or entrance axes. Good for gradual reveal, widening instrumentation, or staged permissions.
Song as negotiated transport
sections:
- id: seduction
semantic: seduction
- id: escalation
semantic: escalation
- id: baseball_broadcast_frenzy
semantic: baseball_broadcast_frenzy
- id: negotiation
semantic: negotiation
- id: vow_under_duress
semantic: vow_under_duress
- id: bitter_aftershock
semantic: bitter_aftershock
This pattern treats time advancement as governed by authority and hold policies.
Useful axes:
form_hold_policy
authority_holder
opening_primitive
Song as participation field
field_actors:
- id: chanters
kind: aggregate_instrument
This pattern lets the audience or environment become an actor.
Useful axes:
participation_state
chant_authority
ambiguity_pressure
Authoring guidance
Prefer explicit guidance over note parsing
Good:
rules:
- when:
section_semantic: clarity
set:
charm_pressure: held
dream_suspension: suspended
blend_authority: dominant
Less ideal:
arc:
- note: charm=Held;suspend=Suspended;blend=Dominant
Notes are useful, but executable guidance should be explicit where possible.
Keep casing machine-friendly
Prefer:
authority_holder: audience_pressure
form_hold_policy: loop_until_released
Avoid executable values like:
AudiencePressure
LoopUntilReleased
Human-facing notes may use prose or title case, but guidance values should match axis options.
Decide what is canonical
Avoid having arc.note and rules.set disagree.
If both describe the same state, define one as authoritative.
Recommended policy:
sectionsdefine time.rulesdefine executable guidance.arcdefines meaningful transitions and optional extra cues.metricsdefine validation expectations.
Declare axes for reusable categorical fields
If a field is used by many renderers or metrics, make it an axis.
For example, this should likely be an axis:
blend_authority: dominant
This may not need to be an axis:
release_cause: scale_alignment_reached
Metrics should test resolved behavior
A metric like this:
- kind: guidance_presence
field: entrance_authority
should pass if entrance_authority is supplied by any valid resolution layer, not only by direct rule assignment.
Validation levels
Consumers may implement multiple validation levels.
Level 1: Schema validation
Checks that the document matches the JSON Schema.
Examples:
- Required top-level fields exist.
apiVersionis correct.kindis correct.- Axis options have ranks.
- Rule objects have
whenandset.
Level 2: Referential validation
Checks references and internal consistency.
Examples:
- Every
arc.section_idexists insections. - Every
metric.when_sectionexists insections. - Every
ordered_monotonic.axisexists inaxes. - Every section has usable timing.
Level 3: Semantic validation
Checks whether resolved frames satisfy declared expectations.
Examples:
suggested_pop_tension > 0.8duringvow_under_duress.scale_pressurerank does not decrease.silence_policyis present duringinstant_awe.
Level 4: Consumer validation
Checks renderer- or simulation-specific requirements.
Examples:
- MIDI renderer requires
tempoand either achord_loopor fallback key. - Visualizer requires section timing.
- Transport simulator requires
form_hold_policy.
Example: minimal POP
apiVersion: net.plantange/v1
kind: simulation.pop
ensemble: tiny.pop
meta:
title: Tiny POP
uuid: tiny-pop-v1
tempo: 120.0
steps_per_beat: 4
beats_per_bar: 4
source:
kind: authored
generator: null
generator_version: null
discovery_ref: null
region_ref: null
musician_ref: null
seed: null
inputs_hash: null
substrate:
chord_loop: [C, F, G]
key_root: C
loop_duration_beats: 12.0
tempo_feel: simple
tonal_mode: major
sections:
- id: opening
semantic: opening
start_s: 0.0
end_s: 20.0
- id: lift
semantic: lift
start_s: 20.0
end_s: 40.0
arc:
- id: opening_entry
section_id: opening
start_s: 0.0
note: Establish the world.
- id: lift_entry
section_id: lift
start_s: 20.0
note: Increase density and brightness.
axes:
- id: scale_pressure
value_kind: ordered_categorical
options:
- value: opening
rank: 1
- value: lifted
rank: 2
default: opening
description: How large the field feels.
rules:
- when:
section_semantic: opening
set:
scale_pressure: opening
suggested_pop_tension: 0.3
- when:
section_semantic: lift
set:
scale_pressure: lifted
suggested_pop_tension: 0.7
metrics:
- id: lift_tension_present
kind: chem_threshold
path: pop.suggested_pop_tension
op: gt
value: 0.6
when_section: lift
field_actors: []
exports:
arc_marker_signal: pop_arc_marker
guidance_signal: pop_guidance
Compatibility notes
The top-level schema is closed with additionalProperties: false. Do not add new top-level fields casually.
The following areas are intentionally extensible:
substratesectionsitem objectsarcitem objects- rule
whenselectors - rule
setpatches exports
This creates a useful split:
- Top-level document shape stays stable.
- Domain-specific interpretation can evolve inside known extension pockets.
Practical consumer contract
A consumer does not need to understand every semantic field to be useful.
A minimal consumer only needs to:
- Load the document.
- Resolve current section by time.
- Apply matching rules.
- Emit guidance.
A richer consumer can additionally:
- parse arc notes,
- evaluate metrics,
- apply field actor contributions,
- render MIDI or visuals,
- simulate transport authority,
- generate logs and traces,
- expose
pop.*chems to agents.