Export contracts¶
This page collects the file formats behind the predictor: the shipped Mood bundle, the knowledge pack and its reader in the core library, and the static source-description record that every analysis starts from. The JSON output page explains the recipe and evidence fields one by one; the Predictor overview explains how they fit together.
Preset-attributes bundle (shipped Mood facets)¶
The app's Mood panel reads a bundle in core/src/main/assets/preset-attributes/. Since 2026-10-16 the bundle has been generated entirely from static source analysis by the predictor on the research branch feat/predictor-static-output-bounds, and no value in it comes from a render. The main branch does not run the predictor. It carries the committed evidence the table was built from and checks the table against that evidence.
| File | Content |
|---|---|
facets.json |
The definition (schema: projectm-tv-mood-facets, version: 1): groups, option ids and labels, rules, and the combination rule (OR within a group, AND across groups). Source: tools/milk-analyzer/facets/facets-v1.json. The palette group (ids red … pink, rule {"palette": family}) and the main_colour group (ids main_red … main_pink, rule {"main_colour": family}) read the preset's colour tags |
attributes.tsv |
Header lines #projectm-tv-preset-attributes<TAB>1, #definition<TAB><sha256 of facets.json>, one #group<TAB>id<TAB>label and #option<TAB>group<TAB>id<TAB>label line per group and option, then #columns<TAB>name<TAB>options<TAB>luminance<TAB>saturation<TAB>coloured_share<TAB>warm_cool<TAB>flow<TAB>frame_difference<TAB>jumps<TAB>activity. Each data row is the preset file name, its comma-separated option ids in definition order and eight static estimates (4 decimals, - for none). The app reads only the options; the parser checks the column count |
manifest.json |
schema, version, source: static-analysis, preset_count, measured_count (rows with any value), analysis_errors, per-option option_counts, SHA-256 of attributes.tsv, facets.json, presets.idx and the static evidence, and the evidence producer (research commit, calibration model SHA-256, policy) |
| Static evidence (tools only) | docs/superpowers/evidence/static-mood-table-2026-10-16/static-evidence.json.gz: gzip JSON {schema: projectm-tv-static-mood-evidence, version, producer, rows}, one row per bundled preset with name, preset sha256, static options (without colour ids), palette (hue families with at least 10% of the static display palette), dominant (a family only when the predicted top-colour share is at least 0.65 and the palette's top share at least 0.55, else null) and the eight values |
Build and verify (standard library only; CI runs --check):
python3 tools/milk-analyzer/attribute_bundle.py --build-static docs/superpowers/evidence/static-mood-table-2026-10-16/static-evidence.json.gz
python3 tools/milk-analyzer/attribute_bundle.py --check
A static build requires exactly one evidence row per presets.idx preset and no row for any other preset. It hashes every preset (in --presets-dir, default presets/ next to --presets-idx) against the row's sha256, so an edited, added or renamed preset fails until it is analysed again. The build also fails on an evidence option or colour that the definition does not know. Palette and Main colour options come from palette and dominant; a preset whose static options include monochrome gets neither. A preset whose analysis failed gets no options and - values, and the build fails if there are any such presets (the limit is 0). The deflated attributes.tsv must stay within 450,000 bytes.
For a manifest with source: static-analysis, --check verifies:
facets.jsonagainst the definition;- the manifest hashes and the header;
- that the rows name exactly the
presets.idxpresets; - that every row equals the row rebuilt from the evidence (
--static-evidence, default above, whose SHA-256 must match the manifest); - every preset's bytes against the evidence;
- the size budget;
- the manifest's
schema,version,source,producer,preset_count,measured_count,analysis_errorsandoption_counts, recomputed from the rows.
tools/milk-analyzer/preset_facets.py implements the selection semantics (counts, toggled counts, dimming) and shares test vectors with the Java PresetFacets class in the core library. The core also exposes ProjectMJNI.setPresetPool(String[]); the legacy setMusicCategory("chill"|"normal"|"intense") is unchanged.
Earlier engine-ruler bundle (history)¶
Until 2026-10-16 the table held engine-ruler measurements (tools/native-ruler/, 300 frames at 480x270 on macOS GLES under SwiftShader) and source style facts. attribute_bundle.py keeps that path (--ruler, --style, --ruler-manifest, --write-evidence-manifest, --freeze-activity-reference). For a manifest without source, --check still verifies it against the ruler results manifest (docs/superpowers/evidence/mood-facets-ruler-2026-10-12/ruler-results-manifest.json) and the frozen activity reference (tools/milk-analyzer/facets/activity-reference-v1.json), recomputing memberships from the stored values. That path no longer produces the shipped bundle.
Preset knowledge pack reader (core library)¶
A preset knowledge pack (preset-knowledge-pack-v1) holds source-only recipes
for a whole preset collection: effects, audio reactivity, calibrated
brightness and motion estimates, and facets. It is plain gzip, JSON and
SHA-256, so any platform can read it. The reader contract is
tools/milk-analyzer/export-contract/knowledge-pack/FORMAT.md:
- check the pack and recipe policy strings;
- check the core file hash, and each line's hash over the stored line bytes;
- require exactly one valid gzip member (header CRC when present, CRC-32 and
size trailer), a
presetsarray and LF line endings (no CR, final LF present); - decode UTF-8 strictly;
- check the JSON types of the fields it exposes (a missing or
nullcontainer means empty); - keep manifest order;
- ignore fields you do not know;
- hand out nothing from a rejected pack.
The core library's PresetKnowledgePack implements it for the app and for
Milkbeat. read(File), read(File, visitor) and read(manifest, core,
visitor) read the compressed core once into memory, verify its file hash,
then decompress it line by line, check every line before parsing it, and keep
only a per-preset view:
- effects with their parameters;
- reactivity as band, target, kind and response;
- mean luminance and frame difference, each with an interval;
- activity and activity bands;
- facet options and values;
- energy tags.
A RecipeVisitor receives each full parsed recipe once, from the same
verified bytes, and only after the whole pack has passed; a rejected pack
publishes nothing. Memory use is the compressed core plus the views, and the
decompressed recipes are never all held at once. load(AssetManager, dir)
returns null for a missing or invalid pack. A pack that breaks a rule
throws InvalidPackException, whose rule names the rule. Lookups work by
preset name or by the preset file's SHA-256.
Every reader shares the same tests: the golden vectors in
export-contract/knowledge-pack/golden-v1/, which are one valid pack, one
with unknown fields, one with null containers, and twelve packs that each
break one rule. PresetKnowledgePackTest runs them on the JVM. Estimates and
facets come from calibrated source analysis and use no rendered frames. No
pack ships in the app yet.
Static source-description contract¶
This record is produced by effect_family_export.py on the research branch feat/predictor-static-output-bounds. It was first published at checkpoint 9c8ff632 (2026-10-10).
The reference documentation archive
preserves the cited contracts and evidence summaries. The implementation
checkpoint is local and is not published by this documentation update. The
following command requires that implementation and its prepared environment;
the archive alone cannot run it.
Use effect_family_export.py from that prepared checkout:
build/preset-lab-venv/bin/python tools/milk-analyzer/effect_family_export.py path/to/preset.milk --output ~/Downloads/ProjectM-TV-static-single
The CLI requires the prepared hash-identified source31 native parser and Python
environment. It consumes no PCM, frame schedule, dimensions or GPU render. Omit
the preset argument for the bundled pack, or supply a folder. Results retain
relative filenames, exact .milk bytes and matched JSON, with a ZIP every 100
completed attempts and a final partial batch. Failures retain analysis: null
and their reason. Resume/cache reuse requires matching source, parser, model and
profile identities; use a fresh process and output folder after code changes.
| JSON location | Meaning |
|---|---|
export_kind |
preset-effect-families; separate from numerical feature records. |
preset and provenance |
Exact preset metadata and parser/model/target identities. |
analysis.schema_version, analysis.analysis_policy |
Semantic schema 1; source-effect-families-v1. |
analysis.families, analysis.stages, analysis.unknowns |
Supported contributing mechanisms, selected/conditional stages and interpretation gaps. |
analysis.visual_description |
Structured baseline traits, schema 1, policy source-appearance-and-control-traits-v1. |
analysis.visual_description.elements |
Source component IDs, numeric construction/colour codes, supported parameters, materials and audio routes. |
| Other description members | Conditional logical composition, sample transforms, colour processing/mixtures and feedback inputs. |
| Execution flags | uses_equation_execution, uses_shader_execution, uses_rendered_images: false. |
| Appearance qualification | appearance_accuracy_verified: false; appearance_match_accuracy: null. Successful extraction is not a visual certificate. |
Semantic record_sha256 members |
Canonical content digests at their respective nested record scopes; not signatures or confidence values. |
The table shows where each contract lives and is not a complete example record. Read the full mechanism reference, appearance fields and numeric dictionaries and appearance JSON Schema for exact object shape, enum meanings, units and unknown variants. The schema alone does not verify hashes, source truth, visibility or reconstruction readiness.
Source audio routes name a component and changed control, engine band, supported source gain/formula and conditions. They do not isolate vocals/instruments or measure visible bass-response strength. Source-time rates describe control math; constant feedback rotation can keep rotating the picture on every feedback step. Selected images, random phases, initial feedback, audio and clock values are additional inputs when a consumer needs an exact instance. Approximate baseline traits can still be useful without them. Final prominence, flash events and complete feedback interpretation remain open implementation and validation work.
A consumer can select an original Rust/wgpu implementation for a supported construction and preserve its known parameters, but it must keep unknowns and its own defaults separate. Recognizable whole-preset reconstruction is still pending validation. Final palettes and calibrated mood options are now produced from this record (see From estimates to moods). The current bounded expression support and work budgets are implementation choices and can be extended. Join records by exact preset SHA-256, and never relabel simulated statistics as static facts.
Earlier numerical feature records (history)¶
Before the static analysis, the predictor simulated presets numerically and exported a 47-field statistical record (the 2026-10-08 checkpoint) with its own JSON Schema, examples and an accuracy rubric. Static analysis replaced that approach, because a simulation that matches the engine explains nothing the engine does not already show and costs seconds to minutes per preset (research journal). The contract, its schema and its examples remain readable in the repository history.