nx_group_composite.nx source
↩ module page · 106 lines · 4284 B
1// nx_group_composite.nx -- aggregate N character verdicts + pair scores into one outcome.
2//
3// For multi-character scenes the substrate produces:
4// - N per-character verdicts (canon score, mannequin index, eroticism rank, etc.)
5// - N*(N-1)/2 pair-synchrony scores between every pair
6//
7// This composite aggregates all of them into ONE scene-quality verdict
8// so the auto_fix loop has a single decision criterion.
9//
10// Per-character contribution: average composite (sum / N).
11// Pair contribution: average pair-synchrony (sum / pair_count).
12// Group composite = weighted average (75% character + 25% pair).
13//
14// Sealed verdict ladder:
15// GROUP_FAILED composite < 410 scene has multiple severe failures
16// GROUP_PARTIAL 410-614 some characters/pairs ok
17// GROUP_COHERENT 614-819 scene reads well
18// GROUP_S_CLASS >= 819 every character + every pair S-class
19
20// nx_safety_envelope:
21// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
22// sil_target: SIL1
23// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
24// verdict: NOT_YET_EVALUATED
25
26import "nx_syscalls.nx"
27const NX_MAGIC_1024: i64 = 1024
28
29const NX_GRP_RES_N_CHARS: i64 = 0
30const NX_GRP_RES_N_PAIRS: i64 = 1
31const NX_GRP_RES_CHAR_AVG: i64 = 2
32const NX_GRP_RES_PAIR_AVG: i64 = 3
33const NX_GRP_RES_WORST_CHAR_SCORE: i64 = 4
34const NX_GRP_RES_WORST_PAIR_SCORE: i64 = 5
35const NX_GRP_RES_COMPOSITE_Q10: i64 = 6
36const NX_GRP_RES_VERDICT: i64 = 7
37const NX_GRP_RES_FIELDS: i64 = 8
38
39const NX_GRP_FAILED: i64 = 0
40const NX_GRP_PARTIAL: i64 = 1
41const NX_GRP_COHERENT: i64 = 2
42const NX_GRP_S_CLASS: i64 = 3
43
44const NX_GRP_WEIGHT_CHARS_PCT: i64 = 75
45const NX_GRP_WEIGHT_PAIRS_PCT: i64 = 25
46
47// Aggregate per-character and per-pair scores.
48// char_scores: i64 array of length n_chars, each entry 0..1024
49// pair_scores: i64 array of length n_pairs (= n_chars*(n_chars-1)/2), each 0..1024
50// result: pre-allocated NX_GRP_RES_FIELDS i64 array
51func nx_group_composite(char_scores: *i64, n_chars: i64,
52 pair_scores: *i64, n_pairs: i64,
53 result: *i64) -> i64 {
54 var i: i64 = 0
55 while i < NX_GRP_RES_FIELDS { result[i] = 0; i = i + 1 }
56 if n_chars <= 0 { return 1 }
57
58 result[NX_GRP_RES_N_CHARS] = n_chars
59 result[NX_GRP_RES_N_PAIRS] = n_pairs
60
61 var char_sum: i64 = 0
62 var worst_char: i64 = NX_MAGIC_1024
63 var ci: i64 = 0
64 while ci < n_chars {
65 let s: i64 = char_scores[ci]
66 char_sum = char_sum + s
67 if s < worst_char { worst_char = s }
68 ci = ci + 1
69 }
70 let char_avg: i64 = char_sum / n_chars
71 result[NX_GRP_RES_CHAR_AVG] = char_avg
72 result[NX_GRP_RES_WORST_CHAR_SCORE] = worst_char
73
74 var pair_avg: i64 = NX_MAGIC_1024 // default to perfect when no pairs (solo scene)
75 var worst_pair: i64 = NX_MAGIC_1024
76 if n_pairs > 0 {
77 var pair_sum: i64 = 0
78 var pi: i64 = 0
79 while pi < n_pairs {
80 let s: i64 = pair_scores[pi]
81 pair_sum = pair_sum + s
82 if s < worst_pair { worst_pair = s }
83 pi = pi + 1
84 }
85 pair_avg = pair_sum / n_pairs
86 }
87 result[NX_GRP_RES_PAIR_AVG] = pair_avg
88 result[NX_GRP_RES_WORST_PAIR_SCORE] = worst_pair
89
90 // Weighted composite: 75% characters + 25% pairs.
91 let composite: i64 = (char_avg * NX_GRP_WEIGHT_CHARS_PCT + pair_avg * NX_GRP_WEIGHT_PAIRS_PCT) / 100
92 result[NX_GRP_RES_COMPOSITE_Q10] = composite
93
94 // Sealed verdict ladder.
95 var verdict: i64 = NX_GRP_FAILED
96 if composite >= 410 { verdict = NX_GRP_PARTIAL }
97 if composite >= 614 { verdict = NX_GRP_COHERENT }
98 if composite >= 819 { verdict = NX_GRP_S_CLASS }
99 // Downgrade if ANY character or pair is severely below threshold.
100 // Even if average is high, one ruined character drags the verdict.
101 if worst_char < 256 { if verdict == NX_GRP_S_CLASS { verdict = NX_GRP_COHERENT } }
102 if worst_char < 256 { if verdict == NX_GRP_COHERENT { verdict = NX_GRP_PARTIAL } }
103 if worst_pair < 256 { if verdict == NX_GRP_S_CLASS { verdict = NX_GRP_COHERENT } }
104 result[NX_GRP_RES_VERDICT] = verdict
105 return 0
106}