Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
78e182a
phase6c: add private MIDI corpus harness
EmergentMonk Oct 1, 2026
a870500
phase6c: formalize MIDI corpus evidence contract
EmergentMonk Oct 1, 2026
c0ba79d
phase6c: add real-world MIDI donor playback probe
EmergentMonk Oct 1, 2026
49c0ddd
phase6c: keep unfrozen Blue Glare track total optional
EmergentMonk Oct 1, 2026
13c713c
phase6c: allow explicitly unfrozen corpus aggregates
EmergentMonk Oct 1, 2026
2c93ab2
phase6c: use canonical FNV-1a offset basis
EmergentMonk Oct 1, 2026
4016983
phase6c: add synthetic MIDI probe fixture
EmergentMonk Oct 1, 2026
d7be061
phase6c: compute MIDI polyphony across SMF tracks
EmergentMonk Oct 1, 2026
70e5c90
phase6c: correct MIDI transition type contract
EmergentMonk Oct 1, 2026
4adfabe
phase6c: bind MIDI corpus support artifacts
EmergentMonk Oct 1, 2026
4e90755
phase6c: add private MIDI real-world corpus workflow
EmergentMonk Oct 1, 2026
c8bb323
phase6c: harden MIDI corpus summary validation
EmergentMonk Oct 1, 2026
2e609cc
phase6c: add MIDI corpus contract tests
EmergentMonk Oct 1, 2026
784249a
phase6c: gate MIDI corpus import and playback projection
EmergentMonk Oct 1, 2026
35db134
phase6c: regress MIDI corpus workflow integration
EmergentMonk Oct 1, 2026
86595ff
docs: advance legacy roadmap to MIDI corpus
EmergentMonk Oct 1, 2026
88c105a
phase6c: pin MIDI corpus archive identities
EmergentMonk Oct 1, 2026
ae67857
phase6c: preserve per-stem MIDI tempo-map counts
EmergentMonk Oct 1, 2026
d6c4e00
phase6c: validate per-stem MIDI tempo maps
EmergentMonk Oct 1, 2026
6f97e8c
phase6c: pin per-stem MIDI tempo-map counts
EmergentMonk Oct 1, 2026
a6979a6
phase6c: exercise cross-track MIDI polyphony
EmergentMonk Oct 1, 2026
b0bdf1d
phase6c: expose MIDI projection probe failures
EmergentMonk Oct 1, 2026
71a2336
phase6c: restore retained MIDI file loader gate
EmergentMonk Oct 1, 2026
494fb3e
phase6c: regress retained MIDI loader gate
EmergentMonk Oct 1, 2026
a31759c
docs: record restored retained MIDI loader gate
EmergentMonk Oct 1, 2026
3fb6bbe
phase6c: harden MIDI corpus identity and interval metrics
EmergentMonk Oct 1, 2026
80e70d5
phase6c: regress MIDI corpus evidence hardening
EmergentMonk Oct 1, 2026
7f9f76e
phase6c: initialize MIDI note pattern entries
EmergentMonk Oct 1, 2026
7d621a4
phase6c: bind MIDI stems and frozen candidate provenance
EmergentMonk Oct 1, 2026
d3eab4f
phase6c: import candidate identity runtime support
EmergentMonk Oct 1, 2026
4431e20
phase6c: verify MIDI observations from pinned private inputs
EmergentMonk Oct 1, 2026
5691ff4
phase6c: regress pinned MIDI stems and candidate identity
EmergentMonk Oct 1, 2026
509a909
phase6c: bind MIDI observations to exact stem identities
EmergentMonk Oct 1, 2026
2995fc1
phase6c: align candidate representative test identity
EmergentMonk Oct 1, 2026
a76d155
phase6c: keep MIDI test syntax portable
EmergentMonk Oct 1, 2026
cf8b495
phase6c: capture MIDI probe crash backtrace
EmergentMonk Oct 1, 2026
4b88020
phase6c: ignore ended Sampulse foreground voices
EmergentMonk Oct 1, 2026
4dfadff
phase6c: preserve MIDI crash diagnostics
EmergentMonk Oct 1, 2026
c2e5948
phase6c: flush final MIDI note-offs
EmergentMonk Oct 1, 2026
742a3ef
phase6c: attest candidate player with clean rebuild
EmergentMonk Oct 1, 2026
214ab0e
phase6c: keep MIDI note-off flush private
EmergentMonk Oct 1, 2026
5964474
phase6c: keep candidate rebuild identity singular
EmergentMonk Oct 1, 2026
de9edcd
phase6c: gate final MIDI release import
EmergentMonk Oct 1, 2026
f359f6d
phase6c: regress candidate rebuild and MIDI releases
EmergentMonk Oct 1, 2026
62ceb1e
phase6c: expose candidate clean-rebuild attestation
EmergentMonk Oct 1, 2026
c344326
phase6c: run candidate clean-rebuild attestation in CI
EmergentMonk Oct 1, 2026
460f04f
phase6c: regress candidate attestation workflow
EmergentMonk Oct 1, 2026
afc7dce
phase6c: bind donor probe to clean source build
EmergentMonk Oct 1, 2026
9cb27e6
phase6c: harden MIDI corpus provenance and channel coverage
EmergentMonk Oct 1, 2026
5603de2
phase6c: attest canonical rebuild and stabilize MIDI projection
EmergentMonk Oct 2, 2026
6d95e2f
phase6c: close MIDI corpus release and privacy gaps
EmergentMonk Oct 2, 2026
80ecfb6
phase6c: align MIDI corpus contract assertions
EmergentMonk Oct 2, 2026
ac54c65
phase6c: harden MIDI corpus evidence and voice reuse
EmergentMonk Oct 6, 2026
0b8c4a4
phase6c: bind tempo evidence to corpus aggregates
EmergentMonk Oct 6, 2026
1b0520c
phase6c: align tempo summary regression data
EmergentMonk Oct 6, 2026
73a1121
phase6c: bind MIDI semantics and candidate evidence
EmergentMonk Oct 6, 2026
274f790
phase6c: align SysEx reader linkage
EmergentMonk Oct 6, 2026
16b154a
phase6c: bind MIDI note and controller semantics
EmergentMonk Oct 6, 2026
798303d
phase6c: preserve exact MIDI tick positions
EmergentMonk Oct 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 192 additions & 1 deletion .github/workflows/phase6c-legacy-playback-import.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ on:
- 'scripts/phase6c-build-historical-it-observer.py'
- 'scripts/phase6c-validate-historical-it-original.py'
- '.github/workflows/phase6c-historical-it-private.yml'
- 'scripts/phase6c-midi-corpus.py'
- 'tests/phase6c_midi_corpus.py'
- 'tests/phase6c_midi_corpus_probe.c'
- '.github/workflows/phase6c-midi-corpus-private.yml'
- 'cpsycle/**'
- 'psycle-cpp-r12005-sanitized/**'
- '.github/workflows/phase6c-legacy-playback-import.yml'
Expand Down Expand Up @@ -57,6 +61,10 @@ on:
- 'scripts/phase6c-build-historical-it-observer.py'
- 'scripts/phase6c-validate-historical-it-original.py'
- '.github/workflows/phase6c-historical-it-private.yml'
- 'scripts/phase6c-midi-corpus.py'
- 'tests/phase6c_midi_corpus.py'
- 'tests/phase6c_midi_corpus_probe.c'
- '.github/workflows/phase6c-midi-corpus-private.yml'
- 'cpsycle/**'
- 'psycle-cpp-r12005-sanitized/**'
- '.github/workflows/phase6c-legacy-playback-import.yml'
Expand All @@ -83,6 +91,7 @@ jobs:
run: |
python3 tests/phase6c_legacy_module_import.py
python3 tests/phase6c_historical_it.py
python3 tests/phase6c_midi_corpus.py

- name: Generate canonical IT fixture
run: |
Expand Down Expand Up @@ -131,7 +140,7 @@ jobs:
DEBIAN_FRONTEND: noninteractive
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends build-essential pkg-config liblua5.4-dev liblilv-dev
sudo apt-get install -y --no-install-recommends build-essential gdb pkg-config liblua5.4-dev liblilv-dev

- name: Generate canonical IT fixture
run: |
Expand All @@ -143,6 +152,159 @@ jobs:
- name: Build C-Psycle donor player libraries
run: make -C cpsycle/player -j1

- name: Build synthetic MIDI corpus execution probe
shell: bash
run: |
set -euo pipefail
python3 scripts/phase6c-midi-corpus.py write-synthetic \
phase6c-generated/phase6c-midi-synthetic.mid
python3 scripts/phase6c-midi-corpus.py write-regression-fixtures \
phase6c-generated/midi-regressions
gcc -std=gnu11 -g -O0 -fno-omit-frame-pointer \
-Wall -Wextra -Werror=implicit-function-declaration \
-Icpsycle/script/src -Icpsycle/thread/src -Icpsycle/container/src \
-Icpsycle/file/src -Icpsycle/diversalis/src -Icpsycle/src \
-Icpsycle/audio/src -Icpsycle/dsp/src $(pkg-config --cflags lua) \
tests/phase6c_midi_corpus_probe.c \
-o phase6c-generated/phase6c-midi-corpus-probe \
-Lcpsycle/thread/src -Lcpsycle/script/src -Lcpsycle/container/src \
-Lcpsycle/dsp/src -Lcpsycle/audio/src -Lcpsycle/file/src \
-Wl,--no-as-needed -laudio -lthread -llilv-0 -ldsp -lscript -lfile \
-lm $(pkg-config --libs lua) -lpthread -ldl -lstdc++ -lcontainer

- name: Prove synthetic SMF import and non-silent projection
shell: bash
run: |
set -euo pipefail
chmod +x phase6c-generated/phase6c-midi-corpus-probe
set +e
phase6c-generated/phase6c-midi-corpus-probe \
phase6c-generated/phase6c-midi-synthetic.mid \
phase6c-generated/phase6c-midi-synthetic.wav \
> phase6c-generated/phase6c-midi-synthetic.log 2>&1
rc=$?
set -e
cat phase6c-generated/phase6c-midi-synthetic.log
if [[ "$rc" -ne 0 ]]; then
gdb -batch \
-ex 'set pagination off' \
-ex run \
-ex 'thread apply all bt full' \
--args phase6c-generated/phase6c-midi-corpus-probe \
phase6c-generated/phase6c-midi-synthetic.mid \
phase6c-generated/phase6c-midi-synthetic.wav \
> phase6c-generated/phase6c-midi-synthetic.gdb.log 2>&1 || true
cat phase6c-generated/phase6c-midi-synthetic.gdb.log
exit "$rc"
fi
tail -n 1 phase6c-generated/phase6c-midi-synthetic.log \
> phase6c-generated/phase6c-midi-synthetic.json
python3 - <<'PY'
import json
from pathlib import Path
observation = json.loads(
Path("phase6c-generated/phase6c-midi-synthetic.json").read_text(
encoding="utf-8"
)
)
assert observation["schema_version"] == 1
assert observation["phase"] == "6C"
assert observation["contract"] == "legacy-midi-real-world-donor"
assert observation["evidence_role"] == "cpsycle-donor-observation"
assert observation["load_result"] == "accepted"
assert observation["sequence_tracks"] == 2
assert observation["patterns"] == 2
assert observation["imported_notes"] == 2
assert observation["releases"] == 2
assert observation["midi_channel_mask"] == 3
assert observation["release_channel_mask"] == 3
assert observation["machines_before_projection"] == 0
assert observation["projection_notes"] == 2
assert observation["projection_kind"] == "deterministic-sampler"
assert observation["render_frames"] > 0
assert observation["render_peak"] > 0
assert observation["non_silent_projection"] is True
assert observation["parity_status"] == "UNKNOWN"
assert len(observation["import_event_digest_fnv64"]) == 16
PY
python3 - <<'PY'
import importlib.util
import json
import math
from pathlib import Path
import subprocess

spec = importlib.util.spec_from_file_location(
"phase6c_midi_corpus_ci",
Path("scripts/phase6c-midi-corpus.py"),
)
helper = importlib.util.module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(helper)

probe = Path("phase6c-generated/phase6c-midi-corpus-probe").resolve()
root = Path("phase6c-generated/midi-regressions")
cases = {
"zero-duration.mid": (1, 1, 1, 1),
"same-note-multichannel.mid": (2, 2, 3, 3),
"high-note.mid": (1, 1, 1, 1),
"equal-tick-release-reuse.mid": (2, 2, 1, 1),
"repeated-two-note-chords.mid": (126, 126, 3, 3),
"polyphony-64.mid": (64, 64, 1, 1),
"sysex-then-note.mid": (1, 1, 1, 1),
"running-status-after-meta.mid": (1, 1, 1, 1),
"controller-event.mid": (1, 1, 1, 1),
"tick-zero-60bpm.mid": (1, 1, 1, 1),
}
for name, (notes, releases, channel_mask, release_mask) in cases.items():
proc = subprocess.run(
[str(probe), str((root / name).resolve())],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
check=False,
)
print(proc.stdout, end="")
assert proc.returncode == 0, (name, proc.returncode)
lines = [line for line in proc.stdout.splitlines() if line.strip()]
observation = json.loads(lines[-1])
analysis = helper.parse_smf(root / name)
assert observation["imported_notes"] == notes, name
assert observation["releases"] == releases, name
assert observation["midi_channel_mask"] == channel_mask, name
assert observation["release_channel_mask"] == release_mask, name
assert (
observation["note_event_digest_fnv64"]
== analysis["note_event_digest_fnv64"]
), name
assert observation["midi_cc"] == analysis["midi_cc"], name
assert (
observation["midi_cc_digest_fnv64"]
== analysis["midi_cc_digest_fnv64"]
), name
assert math.isclose(
observation["song_bpm"],
analysis["song_bpm"],
abs_tol=0.001,
), name
assert observation["projection_kind"] == "not-run", name
assert observation["non_silent_projection"] is False, name
PY
rm -rf phase6c-generated/midi-regressions
rm -f \
phase6c-generated/phase6c-midi-synthetic.mid \
phase6c-generated/phase6c-midi-synthetic.wav

- name: Upload MIDI crash diagnostics
if: failure()
uses: actions/upload-artifact@v4
with:
name: phase6c-midi-crash-diagnostics
path: |
phase6c-generated/phase6c-midi-synthetic.log
phase6c-generated/phase6c-midi-synthetic.gdb.log
if-no-files-found: warn

- name: Build donor IT loader probe
shell: bash
run: |
Expand Down Expand Up @@ -436,6 +598,34 @@ jobs:
qmake CONFIG-=shared CONFIG+=release psycle-player.pro
make -j2

- name: Attest frozen C++ candidate player
shell: bash
run: |
set -euo pipefail
python3 scripts/phase6c-midi-corpus.py attest-candidate-player \
psycle-cpp-r12005-sanitized/psycle-player/++qmake/psycle-player \
phase6c-generated/candidate-player-attestation.json
python3 - <<'PY'
import json
from pathlib import Path
receipt = json.loads(
Path("phase6c-generated/candidate-player-attestation.json").read_text(
encoding="utf-8"
)
)
assert receipt["schema_version"] == 1
assert receipt["phase"] == "6C"
assert receipt["contract"] == "phase6b-candidate-player-clean-rebuild"
assert receipt["evidence_role"] == "candidate-build-attestation"
assert receipt["candidate_baseline_sha256"] == (
"00cd95562b78303b82e17f62fff4b58622f7c0e78c0b4dd850d448082a53893a"
)
assert receipt["source_revision"] == "SourceForge SVN r12005"
assert receipt["build_target"] == "psycle-player"
assert receipt["clean_rebuild_sha256"] == receipt["player_sha256"]
assert receipt["parity_status"] == "UNKNOWN"
PY

- name: Observe direct IT import boundary
shell: bash
run: |
Expand Down Expand Up @@ -507,6 +697,7 @@ jobs:
phase6c-generated/candidate-psycle-player
phase6c-generated/candidate-direct-import.log
phase6c-generated/candidate-direct-import.json
phase6c-generated/candidate-player-attestation.json
if-no-files-found: error


Expand Down
Loading
Loading