Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
4870d19
⚡ Bolt: [performance improvement] O(1) deduplication in chart exports
seonghobae Sep 8, 2026
46102d9
Trigger CI retry
seonghobae Sep 8, 2026
a8d330c
Trigger CI retry
seonghobae Sep 8, 2026
94cc577
repair(ci): adopt canonical formatter owner by ancestry
seonghobae Sep 8, 2026
db491a9
test(exports): require semantic deduplication identifiers
seonghobae Sep 8, 2026
b838991
refactor(exports): name ordered deduplication state
seonghobae Sep 8, 2026
75f9916
⚡ Bolt: [performance improvement] O(1) deduplication in chart exports
seonghobae Sep 8, 2026
f01b86d
⚡ Bolt: [performance improvement] O(1) deduplication in chart exports
seonghobae Sep 8, 2026
e7eca20
test(exports): require complete semantic export contracts
seonghobae Sep 8, 2026
3f81a30
fix(exports): preserve semantic chart contracts
seonghobae Sep 8, 2026
3b9da96
fix(ci): restore canonical supply-chain policy fixture
seonghobae Sep 8, 2026
e087e64
Acknowledge test and ci fixes
seonghobae Sep 8, 2026
763a70d
test(chart): reproduce benchmark contract drift
seonghobae Sep 8, 2026
79da102
fix(chart): align benchmark with documented method
seonghobae Sep 8, 2026
1898fc4
Trigger CI retry
seonghobae Sep 8, 2026
edbd5d3
Trigger CI retry
seonghobae Sep 8, 2026
884c69a
Trigger CI retry
seonghobae Sep 8, 2026
8c6bcf7
Trigger CI retry
seonghobae Sep 8, 2026
7557259
Trigger CI retry
seonghobae Sep 8, 2026
6484e22
Trigger CI retry
seonghobae Sep 8, 2026
49d6f13
Trigger CI retry
seonghobae Sep 8, 2026
fd4efdc
Trigger CI retry
seonghobae Sep 8, 2026
a121d56
Trigger CI retry
seonghobae Sep 8, 2026
54ed68c
Trigger CI retry
seonghobae Sep 8, 2026
e0dd3d2
Acknowledge measurement constraints
seonghobae Sep 8, 2026
7cfc8d3
Trigger CI retry
seonghobae Sep 8, 2026
8575afd
refactor(exports): stack benchmark evidence on canonical chart owner
seonghobae Sep 10, 2026
2aedc4f
refactor(exports): restack benchmark evidence on repaired chart owner
seonghobae Sep 10, 2026
965f2a0
refactor(exports): inherit canonical formatter stack
seonghobae Sep 10, 2026
528d22b
Trigger CI retry
seonghobae Sep 10, 2026
8da3118
repair(exports): restore benchmark-only evidence lane
seonghobae Sep 22, 2026
0edffa2
refactor(exports): inherit repaired canonical chart owner
seonghobae Sep 22, 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
### Changed

- Changed chart-export role, cue, and priority de-duplication to semantically named insertion-ordered dictionaries, preserving first-occurrence output while replacing repeated linear membership scans with average constant-time key lookups.
- Made the retained chart-export benchmark reproduce its documented 96-section, 24-role, 100-warmup, 1,000-sample method and report per-sample median and p95 latency.
- Consolidated Bandit, dependency audits, supplemental secret checks, and Trivy into one trusted-branch security backstop, delegated CodeQL to GitHub default setup, and removed duplicate local PR security and release-preflight runs.
- Pinned npm `10.9.9` as the approved lockfile generator, activated it through Node-bundled Corepack before dependency consumption, and fail closed unless its bundled `tar` is at least `7.5.19`; primary CI still consumes the committed lock only through frozen `npm ci` validation, rejects mutable npm resolution in the lock gate, requires integrity evidence for public-registry lock entries, and preserves generator-sensitive root `@esbuild/*` peer metadata.

Expand Down
103 changes: 103 additions & 0 deletions services/analysis-engine/tests/benchmark_chart_export.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"""Measure chart-export runtime and traced allocation on a realistic song fixture."""

import statistics
import time
import tracemalloc

from bandscope_analysis.exports.chart import build_chart_text, build_cue_sheet_rows


def make_large_song_fixture(
section_count: int = 96, roles_per_section: int = 24
) -> dict[str, object]:
"""Build a realistic large-song export fixture for benchmarking."""
song_sections: list[dict[str, object]] = []
for section_index in range(section_count):
section_roles: list[dict[str, object]] = []
part_graph_nodes: list[dict[str, object]] = []
for role_index in range(roles_per_section):
role_identifier = f"role_{role_index % 5}"
section_roles.append(
{
"id": role_identifier,
"name": f"Role Name {role_identifier}",
"cue": {"value": f"Cue {role_index % 4}"},
"rehearsalPriority": f"Priority {role_index % 2}",
}
)
part_graph_nodes.append({"role_id": role_identifier, "is_active": True})

song_sections.append(
{
"label": f"Section {section_index}",
"timeRange": {
"start": section_index * 10,
"end": section_index * 10 + 5,
},
"roles": section_roles,
"partGraph": part_graph_nodes,
"confidence": {"level": "high"},
}
)

return {
"title": "Benchmark Large Song",
"bpm": 120,
"key": "C major",
"feel": "Straight",
"sections": song_sections,
"exportSummary": {"headline": "Benchmark"},
}


def chart_export_benchmark() -> None:
"""Print runtime and traced peak allocation for repeated chart exports."""
benchmark_song = make_large_song_fixture()

for _warmup_iteration in range(100):
build_chart_text(benchmark_song)
build_cue_sheet_rows(benchmark_song)

print("Running Latency Benchmark...")

# Phase 1: Pure Latency (no tracemalloc overhead)
benchmark_iteration_count = 1000
benchmark_sample_durations_seconds: list[float] = []
for _benchmark_iteration in range(benchmark_iteration_count):
benchmark_sample_started_at = time.perf_counter()
build_chart_text(benchmark_song)
build_cue_sheet_rows(benchmark_song)
benchmark_sample_finished_at = time.perf_counter()
benchmark_sample_durations_seconds.append(
benchmark_sample_finished_at - benchmark_sample_started_at
)

print("Running Allocation Benchmark...")
# Phase 2: Pure Allocation (no timing structures)
tracemalloc.start()

allocation_iteration_count = 10
for _allocation_iteration in range(allocation_iteration_count):
build_chart_text(benchmark_song)
build_cue_sheet_rows(benchmark_song)

_current_allocation_bytes, peak_allocation_bytes = tracemalloc.get_traced_memory()
tracemalloc.stop()

total_duration_seconds = sum(benchmark_sample_durations_seconds)
median_duration_seconds = statistics.median(benchmark_sample_durations_seconds)
p95_duration_seconds = statistics.quantiles(
benchmark_sample_durations_seconds, n=100, method="inclusive"
)[94]
print(f"Total time for {benchmark_iteration_count} iterations: {total_duration_seconds:.4f}s")
print(
"Average time per iteration: "
f"{(total_duration_seconds / benchmark_iteration_count) * 1000:.2f}ms"
)
print(f"Median time per sample: {median_duration_seconds * 1000:.2f}ms")
print(f"P95 time per sample: {p95_duration_seconds * 1000:.2f}ms")
print(f"Peak memory overhead: {peak_allocation_bytes / 1024 / 1024:.2f} MB")


if __name__ == "__main__":
chart_export_benchmark()
Loading