Skip to content

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:

  1. Load and validate the POP document.
  2. Build a timeline from sections.
  3. At a given time, beat, bar, or frame, determine the current section.
  4. Apply matching rules.
  5. Merge applicable arc marker information.
  6. Expose the resulting state as pop.* runtime chems or guidance.
  7. Allow actors, renderers, musicians, or simulations to respond.
  8. Optionally evaluate metrics.
  9. 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 tempo when rendering music, MIDI, animation, or beat-aligned events.
  • Do not assume section boundaries are bar-aligned.
  • Preserve original start_s and end_s values 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 substrate as 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:

  • semantic
  • start_s
  • end_s
  • bar_start
  • bar_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_s is less than or equal to end_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:

  1. Use seconds when resolving wall-clock simulation.
  2. Use bars when rendering beat/bar-aligned musical material.
  3. 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_id
  • start_s
  • bar
  • note
  • additional consumer-specific fields

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:

  1. Axis default.
  2. Section rule output.
  3. Arc marker guidance.
  4. Field actor contribution.
  5. 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 when keys match the current runtime context.
  • section_id should match the section id.
  • section_semantic should match the section semantic.
  • 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:

  1. Normalize from_axis rank to 0.0..1.0.
  2. Normalize each scale_by axis rank to 0.0..1.0.
  3. Multiply them together.
  4. 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 exports is null, 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 additionalProperties is true.
  • Reject unknown top-level fields, because the top-level schema is closed.

Timeline resolution

  • Sort sections by start_s or bar_start.
  • Resolve current section for each frame.
  • Handle missing sections gracefully 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_presence on resolved frames.
  • Evaluate chem_threshold on resolved chem paths.
  • Evaluate ordered_monotonic using 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.

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:

  • sections define time.
  • rules define executable guidance.
  • arc defines meaningful transitions and optional extra cues.
  • metrics define 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.
  • apiVersion is correct.
  • kind is correct.
  • Axis options have ranks.
  • Rule objects have when and set.

Level 2: Referential validation

Checks references and internal consistency.

Examples:

  • Every arc.section_id exists in sections.
  • Every metric.when_section exists in sections.
  • Every ordered_monotonic.axis exists in axes.
  • Every section has usable timing.

Level 3: Semantic validation

Checks whether resolved frames satisfy declared expectations.

Examples:

  • suggested_pop_tension > 0.8 during vow_under_duress.
  • scale_pressure rank does not decrease.
  • silence_policy is present during instant_awe.

Level 4: Consumer validation

Checks renderer- or simulation-specific requirements.

Examples:

  • MIDI renderer requires tempo and either a chord_loop or 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:

  • substrate
  • sections item objects
  • arc item objects
  • rule when selectors
  • rule set patches
  • 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:

  1. Load the document.
  2. Resolve current section by time.
  3. Apply matching rules.
  4. 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:

  1. Load and validate the POP document.
  2. Build a timeline from sections.
  3. At a given time, beat, bar, or frame, determine the current section.
  4. Apply matching rules.
  5. Merge applicable arc marker information.
  6. Expose the resulting state as pop.* runtime chems or guidance.
  7. Allow actors, renderers, musicians, or simulations to respond.
  8. Optionally evaluate metrics.
  9. 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 tempo when rendering music, MIDI, animation, or beat-aligned events.
  • Do not assume section boundaries are bar-aligned.
  • Preserve original start_s and end_s values 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 substrate as 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:

  • semantic
  • start_s
  • end_s
  • bar_start
  • bar_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_s is less than or equal to end_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:

  1. Use seconds when resolving wall-clock simulation.
  2. Use bars when rendering beat/bar-aligned musical material.
  3. 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_id
  • start_s
  • bar
  • note
  • additional consumer-specific fields

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:

  1. Axis default.
  2. Section rule output.
  3. Arc marker guidance.
  4. Field actor contribution.
  5. 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 when keys match the current runtime context.
  • section_id should match the section id.
  • section_semantic should match the section semantic.
  • 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:

  1. Normalize from_axis rank to 0.0..1.0.
  2. Normalize each scale_by axis rank to 0.0..1.0.
  3. Multiply them together.
  4. 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 exports is null, 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 additionalProperties is true.
  • Reject unknown top-level fields, because the top-level schema is closed.

Timeline resolution

  • Sort sections by start_s or bar_start.
  • Resolve current section for each frame.
  • Handle missing sections gracefully 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_presence on resolved frames.
  • Evaluate chem_threshold on resolved chem paths.
  • Evaluate ordered_monotonic using 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.

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:

  • sections define time.
  • rules define executable guidance.
  • arc defines meaningful transitions and optional extra cues.
  • metrics define 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.
  • apiVersion is correct.
  • kind is correct.
  • Axis options have ranks.
  • Rule objects have when and set.

Level 2: Referential validation

Checks references and internal consistency.

Examples:

  • Every arc.section_id exists in sections.
  • Every metric.when_section exists in sections.
  • Every ordered_monotonic.axis exists in axes.
  • Every section has usable timing.

Level 3: Semantic validation

Checks whether resolved frames satisfy declared expectations.

Examples:

  • suggested_pop_tension > 0.8 during vow_under_duress.
  • scale_pressure rank does not decrease.
  • silence_policy is present during instant_awe.

Level 4: Consumer validation

Checks renderer- or simulation-specific requirements.

Examples:

  • MIDI renderer requires tempo and either a chord_loop or 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:

  • substrate
  • sections item objects
  • arc item objects
  • rule when selectors
  • rule set patches
  • 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:

  1. Load the document.
  2. Resolve current section by time.
  3. Apply matching rules.
  4. 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.