Validation
ci / checks (push) Failing after 43m34s

This commit is contained in:
khodowany
2026-10-05 20:33:14 -05:00
parent e7180e557b
commit ffdbe48258
22 changed files with 541 additions and 409 deletions
+15 -2
View File
@@ -91,8 +91,11 @@ def validate(notes,contracts,harmony):
return {},c.id+': missing or changed expression event/target'
if c.preparation and not same(c.preparation):return {},c.id+': changed preparation'
if any(n.event_id in approved for n in c.events):return {},c.id+': overlapping expression permissions'
target=c.target; span=harmony.at(c.boundary if c.boundary is not None else target.start)
target=c.target; span=harmony.at(c.boundary if c.boundary is not None else c.events[0].start if c.kind=='color' else target.start)
if not span or span.chord=='N.C.' or span.chord!=c.chord:return {},c.id+': wrong target harmony'
if c.kind in ('anticipation','anticipated_color'):
previous=next((s for s in harmony.spans if s.end==c.boundary),None)
if not previous or previous.chord.strip('()')==span.chord.strip('()'):return {},c.id+': anticipation requires a harmonic change'
if c.kind in ('approach','enclosure','connection'):
rule=next((r for r in vocabulary.data('lead_ins')['patterns'] if r['id']==c.rule and r['kind']==c.kind),None)
if not rule or len(c.events)!=len(rule['semitones']):return {},c.id+': unknown approach formula'
@@ -112,6 +115,16 @@ def validate(notes,contracts,harmony):
if n.pitch.number()%12 not in harmony.permitted(n.start,n.end):return {},c.id+': declared diatonic neighbor is outside source-key/chord motion'
if any(n.start<c.target.start and n.end>c.events[0].start and n.event_id not in {e.event_id for e in c.events} for n in notes):return {},c.id+': interrupted approach'
if c.kind=='connection' and (not c.preparation or c.preparation.end!=c.events[0].start or abs(c.preparation.pitch.number()-c.events[0].pitch.number())>2):return {},c.id+': chromatic connection lacks its incoming motion'
elif c.kind=='passing':
rule=vocabulary.data('lead_ins')['passing']
if c.rule!=rule['id'] or len(c.events)!=1 or not c.preparation:return {},c.id+': unsupported passing figure'
n,=c.events;before=c.preparation
left=n.pitch.number()-before.pitch.number();right=target.pitch.number()-n.pitch.number()
if before.end!=n.start or n.end!=target.start or n.end-n.start>F(rule['max_note']) or left!=right or left not in rule['steps']:
return {},c.id+': interrupted/nonstepwise passing figure'
def supported(endpoint):
return harmony.ordinary(endpoint) or any(a.kind in ('color','anticipated_color') and endpoint in a.events for a in contracts)
if not supported(before) or not supported(target):return {},c.id+': unsupported passing endpoints'
elif c.kind=='delayed_approach':
rule=vocabulary.data('lead_ins').get('delayed_approach',{})
if c.rule!=rule.get('id') or len(c.events)!=1:return {},c.id+': unknown delayed approach'
@@ -126,7 +139,7 @@ def validate(notes,contracts,harmony):
for n in c.events:
if n.end-n.start>F(rule['max_beats']):return {},c.id+': excessive color duration'
for region,a,b in harmony.segments(n):
if n.pitch.number()%12 in region.roles.basic:continue
if n.pitch.number()%12 in region.roles.basic|region.roles.specified:continue
if region.chord=='N.C.' or not eligible_color(rule,region,harmony):return {},c.id+': incompatible color harmony/function'
p=degree_pitch(parsed_chord(region.chord),rule['degree'],rule['alter'],n.pitch.number())
# The rule retains the functional degree/spelling. Encoded