nx_directors_note.nx source
↩ module page · 452 lines · 19718 B
1// nx_directors_note.nx -- the canonical "director's note" envelope.
2//
3// Per cardinal feedback-arc-and-moment-are-king-cinematographer-pattern:
4// arc + moment ARE the director. Everything downstream (scene composer,
5// prompt builder, wardrobe, gen-img, quality gate) is CREW executing
6// the director's vision. Falling back to a downstream service's
7// defaults = ignoring the director = the 3000-batch sprawl.
8//
9// This primitive is the typed envelope that flows through the entire
10// render pipeline. Every service consumes a *DirectorsNote (never a
11// flat dict), reads what it needs, raises NX_DN_UNDERSPECIFIED if a
12// required field is unset. No service is allowed to silently default.
13//
14// Per cardinal feedback-new-capabilities-go-to-nishilang-not-python:
15// this lives in NishiLang, not in Python. Elder AI's Python services
16// will subprocess-consume the compiled binary (or HTTP-call a NishiLang
17// micro-service wrapping it) as the migration progresses.
18//
19// Per cardinal feedback-audit-before-build-togaf-into-complete-system:
20// audited existing nx_ primitives 2026-05-15 — no current primitive
21// owns this concept. `nx_mise_en_scene` is render-side scoring (not
22// the authority). `nx_sketch_moments` is statistical moments (not
23// narrative). `nx_beauty_score` ships the AwardClass enum we compose
24// against here, not duplicate.
25//
26// Per cardinal feedback-linux-clear-tools-not-sprawl: this is ONE tool
27// with ONE job — carry the director's intent end-to-end. No business
28// logic; just the typed envelope + validators + builders.
29//
30// === Composition ==================================================
31//
32// nx_beauty_score.nx AwardClass constants (S/A/B/OFF_CANON)
33// nx_canon_proportions proportion canon (substrate of anatomy)
34// nx_string_ops buf+len string handling for names/keys
35// nx_result typed OK/ERR envelope
36// nx_tier nx_int / nx_size aliases
37//
38// genealogy_id: aristotle_unities + chomsky_grammar + brecht_alienation_effect +
39// eisenstein_montage + propp_morphology + houdini_director_sop
40// lineage_id: nx_directors_note_v1
41
42// nx_safety_envelope:
43// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
44// sil_target: SIL1
45// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
46// verdict: NOT_YET_EVALUATED
47
48import "nx_syscalls.nx"
49import "nx_tier.nx"
50
51// ===== Sealed-enum: DirectorRole ==================================
52//
53// Who is directing this moment. The substrate is content-domain-
54// agnostic; what makes a moment coherent is that ONE director is in
55// charge of it.
56
57const NX_DR_EXTERNAL_USER: nx_int = 0 // human user typed an explicit request
58const NX_DR_EXTERNAL_SYSTEM: nx_int = 1 // batch driver, scheduler, scene planner
59const NX_DR_COMPANION: nx_int = 2 // companion (Elara) authored from her own decisions
60const NX_DR_ARC_AUTHORED: nx_int = 3 // hand-curated arc-stage author
61const NX_DR_DYNAMIC_LLM: nx_int = 4 // LLM-generated arc
62const NX_DR_N_ROLES: nx_int = 5
63
64func nx_dr_role_is_valid(r: nx_int) -> nx_int {
65 if r < 0 { return 0 }
66 if r >= NX_DR_N_ROLES { return 0 }
67 return 1
68}
69
70// ===== Sealed-enum: ContentDomain ==================================
71//
72// The content space the moment lives in. Substrate is domain-agnostic;
73// downstream services adapt their vocabulary via this tag. This is
74// what lets the same envelope ferry MUNDANE study sessions, FANTASY
75// quests, EROTIC scenes, RPG combats through the SAME pipeline.
76
77const NX_CD_MUNDANE: nx_int = 0 // study, cooking, reading, dressing
78const NX_CD_INTIMATE: nx_int = 1 // romance, affection, sensual
79const NX_CD_EROTIC: nx_int = 2 // explicit sexual content
80const NX_CD_FANTASY: nx_int = 3 // magic, mythical, otherworldly
81const NX_CD_RPG: nx_int = 4 // combat, exploration, dialogue
82const NX_CD_NARRATIVE: nx_int = 5 // story beats, character moments
83const NX_CD_N_DOMAINS: nx_int = 6
84
85func nx_cd_domain_is_valid(d: nx_int) -> nx_int {
86 if d < 0 { return 0 }
87 if d >= NX_CD_N_DOMAINS { return 0 }
88 return 1
89}
90
91// ===== Sealed-enum: AffectAxis ====================================
92//
93// The character's emotional posture for this moment. TYPED axis the
94// director sets, not an unscored float. Substrate can route subtle
95// drift on this axis to DRIFT tier, not BROKEN, per the graduated-
96// intervention cardinal.
97
98const NX_AF_NEUTRAL: nx_int = 0
99const NX_AF_VULNERABLE: nx_int = 1
100const NX_AF_PLAYFUL: nx_int = 2
101const NX_AF_DOMINANT: nx_int = 3
102const NX_AF_INTIMATE: nx_int = 4
103const NX_AF_ANXIOUS: nx_int = 5
104const NX_AF_JOYFUL: nx_int = 6
105const NX_AF_MELANCHOLIC: nx_int = 7
106const NX_AF_FOCUSED: nx_int = 8 // studying, working, reading
107const NX_AF_REVERENT: nx_int = 9 // awe, worship, profound moment
108const NX_AF_PLAYFUL_BRATTY: nx_int = 10
109const NX_AF_N_AXES: nx_int = 11
110
111func nx_af_axis_is_valid(a: nx_int) -> nx_int {
112 if a < 0 { return 0 }
113 if a >= NX_AF_N_AXES { return 0 }
114 return 1
115}
116
117// ===== Sealed-enum: SourceTag =====================================
118//
119// Where this moment's content came from. Lets the substrate trace
120// back to source and propose pool-expansion when something fails.
121
122const NX_ST_STAGE_POOL: nx_int = 0 // picked from arc stage's pool
123const NX_ST_CONSTELLATION: nx_int = 1 // pre-validated cluster
124const NX_ST_EROTIC_SCENARIO: nx_int = 2 // choreography source
125const NX_ST_CHAT_OVERRIDE: nx_int = 3 // user typed an override
126const NX_ST_COMPANION_DECISION: nx_int = 4 // Elara picked this
127const NX_ST_DYNAMIC_EXPANSION: nx_int = 5 // LLM-expanded for novelty
128const NX_ST_POV_SCENARIO: nx_int = 6 // POV framing source
129const NX_ST_N_TAGS: nx_int = 7
130
131func nx_st_tag_is_valid(t: nx_int) -> nx_int {
132 if t < 0 { return 0 }
133 if t >= NX_ST_N_TAGS { return 0 }
134 return 1
135}
136
137// ===== Sealed-enum: AwardClass (compose with nx_beauty_score) ======
138//
139// nx_beauty_score.nx already defines NX_BEAUTY_S_CLASS / A_CLASS /
140// B_CLASS / OFF_CANON / UNKNOWN constants. Rather than duplicate,
141// we re-export them under the DN_AWARD_ prefix so callers reading
142// this file see the full envelope vocabulary in one place. The
143// underlying values are identical and the predicate composes.
144
145const NX_DN_AWARD_UNVERIFIED: nx_int = 0 // = NX_BEAUTY_UNKNOWN
146const NX_DN_AWARD_S_CLASS: nx_int = 1 // = NX_BEAUTY_S_CLASS
147const NX_DN_AWARD_A_CLASS: nx_int = 2 // = NX_BEAUTY_A_CLASS
148const NX_DN_AWARD_B_CLASS: nx_int = 3 // = NX_BEAUTY_B_CLASS
149const NX_DN_AWARD_OFF_CANON: nx_int = 4 // = NX_BEAUTY_OFF_CANON
150const NX_DN_AWARD_N_CLASSES: nx_int = 5
151
152func nx_dn_award_is_valid(a: nx_int) -> nx_int {
153 if a < 0 { return 0 }
154 if a >= NX_DN_AWARD_N_CLASSES { return 0 }
155 return 1
156}
157
158// ===== Sealed-enum: PreflightTier =================================
159//
160// Per cardinal feedback-graduated-intervention-no-chainsaw: three
161// sealed verdicts not binary pass/fail.
162
163const NX_PFT_GOOD: nx_int = 0 // ship, archive as exemplar
164const NX_PFT_DRIFT: nx_int = 1 // ship + log trend, NO regen
165const NX_PFT_BROKEN: nx_int = 2 // block + regen with corrective phrasing
166const NX_PFT_N_TIERS: nx_int = 3
167
168func nx_pft_tier_is_valid(t: nx_int) -> nx_int {
169 if t < 0 { return 0 }
170 if t >= NX_PFT_N_TIERS { return 0 }
171 return 1
172}
173
174// ===== Sealed-enum: Underspecified error codes ====================
175//
176// When a downstream service finds a needed field unset, it returns
177// the specific code so moment_generator can expand its vocabulary.
178
179const NX_DN_UNDERSPEC_NONE: nx_int = 0
180const NX_DN_UNDERSPEC_OUTFIT: nx_int = 1
181const NX_DN_UNDERSPEC_LOCATION: nx_int = 2
182const NX_DN_UNDERSPEC_POSE: nx_int = 3
183const NX_DN_UNDERSPEC_AFFECT: nx_int = 4
184const NX_DN_UNDERSPEC_LIGHTING: nx_int = 5
185const NX_DN_UNDERSPEC_CAMERA: nx_int = 6
186const NX_DN_UNDERSPEC_ACTIVITY: nx_int = 7
187const NX_DN_UNDERSPEC_CHARACTER: nx_int = 8
188const NX_DN_UNDERSPEC_BODY_ARCHETYPE: nx_int = 9
189const NX_DN_UNDERSPEC_DOMAIN: nx_int = 10
190const NX_DN_UNDERSPEC_N_CODES: nx_int = 11
191
192func nx_dn_underspec_is_valid(c: nx_int) -> nx_int {
193 if c < 0 { return 0 }
194 if c >= NX_DN_UNDERSPEC_N_CODES { return 0 }
195 return 1
196}
197
198// ===== Sealed-enum: Perspective + ShotSize (cinematography) =======
199//
200// Promoted from Python's svc-arc-moments/models.py into the canonical
201// NishiLang home. Same semantics; integer-backed so they compose
202// with the sealed-enum substrate convention.
203
204const NX_PERSP_POV: nx_int = 0 // she's engaging with you
205const NX_PERSP_POV_BOYFRIEND: nx_int = 1 // boyfriend POV, intimate framing
206const NX_PERSP_VOYEUR: nx_int = 2 // you're watching unnoticed
207const NX_PERSP_CANDID: nx_int = 3 // candid/natural, aware but not posing
208const NX_PERSP_SELFIE: nx_int = 4 // she's taking a selfie
209const NX_PERSP_THIRD: nx_int = 5 // cinematic third-person
210const NX_PERSP_N_PERSPS: nx_int = 6
211
212func nx_persp_is_valid(p: nx_int) -> nx_int {
213 if p < 0 { return 0 }
214 if p >= NX_PERSP_N_PERSPS { return 0 }
215 return 1
216}
217
218const NX_SHOT_ECU: nx_int = 0 // extreme close-up (eyes, lips)
219const NX_SHOT_BCU: nx_int = 1 // big close-up (face fills frame)
220const NX_SHOT_CU: nx_int = 2 // close-up (head and shoulders)
221const NX_SHOT_MCU: nx_int = 3 // medium close-up (chest up)
222const NX_SHOT_MS: nx_int = 4 // medium shot (waist up)
223const NX_SHOT_MWS: nx_int = 5 // medium wide shot (knees up)
224const NX_SHOT_WS: nx_int = 6 // wide shot (full body)
225const NX_SHOT_FS: nx_int = 7 // full shot (full body with environment)
226const NX_SHOT_N_SHOTS: nx_int = 8
227
228func nx_shot_is_valid(s: nx_int) -> nx_int {
229 if s < 0 { return 0 }
230 if s >= NX_SHOT_N_SHOTS { return 0 }
231 return 1
232}
233
234// ===== BodyMark struct ============================================
235//
236// A persistent physical mark on the actor (hickey, sweat, tears,
237// wetness). Carries across moments within an arc so continuity
238// holds visually. Per additive-only-data cardinal: marks accumulate;
239// is_current=0 preserves history without delete.
240
241struct BodyMark {
242 mark_kind: nx_int, // sealed enum BMK_HICKEY/SWEAT/TEARS/WETNESS/...
243 location_id: nx_int, // sealed enum (neck_left, thigh_inner, lips, ...)
244 intensity_q10: nx_int, // 0..1024
245 applied_at_seq: nx_int, // moment sequence index that created the mark
246 is_current: nx_int // 1 = active, 0 = historical
247}
248
249const NX_BMK_HICKEY: nx_int = 0
250const NX_BMK_SWEAT: nx_int = 1
251const NX_BMK_TEARS: nx_int = 2
252const NX_BMK_WETNESS: nx_int = 3
253const NX_BMK_FLUSH: nx_int = 4
254const NX_BMK_SALIVA: nx_int = 5
255const NX_BMK_N_KINDS: nx_int = 6
256
257func nx_bmk_kind_is_valid(k: nx_int) -> nx_int {
258 if k < 0 { return 0 }
259 if k >= NX_BMK_N_KINDS { return 0 }
260 return 1
261}
262
263// ===== DirectorsNote struct ========================================
264//
265// The CANONICAL envelope. Carried unchanged through every step of
266// the render pipeline. Built by moment_generator (or a companion
267// authoring her own decision), consumed by every downstream service.
268//
269// All ID fields are i64 IDs (interned keys, not strings) — keeps the
270// struct fixed-size + compatible with NishiLang's no-string-storage
271// substrate. The Python facade resolves IDs to strings on emit; the
272// substrate-side primitive operates on IDs.
273
274struct DirectorsNote {
275 // === Identity =================================================
276 arc_id: nx_int,
277 arc_stage: nx_int,
278 moment_seq: nx_int, // monotone per-arc sequence number
279 character_id: nx_int,
280
281 // === Provenance + authority ===================================
282 director_role: nx_int, // DR_EXTERNAL_USER / COMPANION / ...
283 source_tag: nx_int, // ST_STAGE_POOL / CONSTELLATION / ...
284 content_domain: nx_int, // CD_MUNDANE / EROTIC / FANTASY / ...
285
286 // === Actor's state ===========================================
287 outfit_key_id: nx_int, // -> outfits table; substrate resolves
288 affect_axis: nx_int, // AF_FOCUSED / PLAYFUL / VULNERABLE / ...
289 arousal_q10: nx_int, // 0..1024 (so any caller can scale)
290 body_archetype_id: nx_int, // tall_feminine_height / ... (the user's "we like" cluster)
291 exposure_level_q10: nx_int, // 0..1024 (bridges categorical wardrobe_state)
292
293 // === Scene =====================================================
294 location_id: nx_int, // -> locations table
295 activity_id: nx_int, // -> activities table
296 pose_id: nx_int, // -> poses table (renamed from body_position)
297 time_of_day: nx_int, // sealed enum
298 lighting_mood: nx_int, // sealed enum (warm/dramatic/soft/...)
299 fg_light_id: nx_int, // -> three-plane lighting (was on CoherentScene)
300 mg_light_id: nx_int,
301 bg_light_id: nx_int,
302
303 // === Camera ===================================================
304 perspective: nx_int, // PERSP_POV / VOYEUR / SELFIE / ...
305 shot_size: nx_int, // SHOT_ECU / CU / MS / WS / FS / ...
306 camera_motivation_id: nx_int, // why the camera is here
307 composition_id: nx_int, // rule_of_thirds / centered / diagonal / ...
308 gaze_direction: nx_int, // sealed: at_camera / away / down / ...
309
310 // === Continuity ==============================================
311 prev_moment_seq: nx_int, // for cross-moment continuity refs
312 constellation_id: nx_int, // pre-validated cluster
313 pov_scenario_id: nx_int, // POV framing source
314
315 // === Quality + verification (per s-class-canon cardinal) ====
316 award_class: nx_int, // DN_AWARD_UNVERIFIED until evaluated
317 preflight_tier: nx_int, // PFT_GOOD / DRIFT / BROKEN; UNVERIFIED until gated
318 refine_iteration: nx_int, // which iteration of the auto-fix loop
319
320 // === Underspec tracking =====================================
321 underspec_code: nx_int // DN_UNDERSPEC_NONE in healthy state
322}
323
324const NX_DN_FIELDS: nx_int = 31 // total fields in DirectorsNote
325const NX_DN_BYTES: nx_int = 248 // = 31 * 8; NishiLang consts must be literals
326
327// ===== Builder ====================================================
328//
329// Allocates a DirectorsNote with neutral defaults (UNDERSPEC_NONE,
330// UNVERIFIED award, refine_iteration=0). Caller MUST set the
331// required fields before passing to a downstream service.
332
333func nx_dn_alloc() -> *DirectorsNote {
334 let dn: *DirectorsNote = (sys_mmap(NX_DN_BYTES)) as *DirectorsNote
335 dn.arc_id = 0
336 dn.arc_stage = 0
337 dn.moment_seq = 0
338 dn.character_id = 0
339 dn.director_role = NX_DR_EXTERNAL_SYSTEM
340 dn.source_tag = NX_ST_STAGE_POOL
341 dn.content_domain = NX_CD_MUNDANE
342 dn.outfit_key_id = 0
343 dn.affect_axis = NX_AF_NEUTRAL
344 dn.arousal_q10 = 0
345 dn.body_archetype_id = 0
346 dn.exposure_level_q10 = 0
347 dn.location_id = 0
348 dn.activity_id = 0
349 dn.pose_id = 0
350 dn.time_of_day = 0
351 dn.lighting_mood = 0
352 dn.fg_light_id = 0
353 dn.mg_light_id = 0
354 dn.bg_light_id = 0
355 dn.perspective = NX_PERSP_VOYEUR
356 dn.shot_size = NX_SHOT_MS
357 dn.camera_motivation_id = 0
358 dn.composition_id = 0
359 dn.gaze_direction = 0
360 dn.prev_moment_seq = 0 - 1
361 dn.constellation_id = 0
362 dn.pov_scenario_id = 0
363 dn.award_class = NX_DN_AWARD_UNVERIFIED
364 dn.preflight_tier = NX_PFT_GOOD
365 dn.refine_iteration = 0
366 dn.underspec_code = NX_DN_UNDERSPEC_NONE
367 return dn
368}
369
370// ===== Validators =================================================
371//
372// Caller checks the envelope before handing off to a downstream
373// service. Returns NX_DN_UNDERSPEC_NONE if the envelope is complete
374// enough to render, else the specific code naming what's missing.
375
376func nx_dn_validate_for_render(dn: *DirectorsNote) -> nx_int {
377 if dn.outfit_key_id == 0 { return NX_DN_UNDERSPEC_OUTFIT }
378 if dn.location_id == 0 { return NX_DN_UNDERSPEC_LOCATION }
379 if dn.pose_id == 0 { return NX_DN_UNDERSPEC_POSE }
380 if dn.activity_id == 0 { return NX_DN_UNDERSPEC_ACTIVITY }
381 if dn.character_id == 0 { return NX_DN_UNDERSPEC_CHARACTER }
382 if dn.body_archetype_id == 0 { return NX_DN_UNDERSPEC_BODY_ARCHETYPE }
383 if nx_dr_role_is_valid(dn.director_role) == 0 { return NX_DN_UNDERSPEC_DOMAIN }
384 if nx_cd_domain_is_valid(dn.content_domain) == 0 { return NX_DN_UNDERSPEC_DOMAIN }
385 if nx_af_axis_is_valid(dn.affect_axis) == 0 { return NX_DN_UNDERSPEC_AFFECT }
386 if nx_persp_is_valid(dn.perspective) == 0 { return NX_DN_UNDERSPEC_CAMERA }
387 if nx_shot_is_valid(dn.shot_size) == 0 { return NX_DN_UNDERSPEC_CAMERA }
388 return NX_DN_UNDERSPEC_NONE
389}
390
391// ===== Accessors with safe defaults ===============================
392//
393// Downstream services use these instead of direct field reads when
394// they tolerate a sentinel. Strict callers use validate_for_render
395// first and refuse to proceed if underspecified.
396
397func nx_dn_award_class(dn: *DirectorsNote) -> nx_int { return dn.award_class }
398func nx_dn_preflight_tier(dn: *DirectorsNote) -> nx_int { return dn.preflight_tier }
399func nx_dn_director_role(dn: *DirectorsNote) -> nx_int { return dn.director_role }
400func nx_dn_content_domain(dn: *DirectorsNote) -> nx_int { return dn.content_domain }
401func nx_dn_affect_axis(dn: *DirectorsNote) -> nx_int { return dn.affect_axis }
402func nx_dn_underspec_code(dn: *DirectorsNote) -> nx_int { return dn.underspec_code }
403
404// ===== Tier router (graduated intervention) =======================
405//
406// Translates an AwardClass into a PreflightTier per the graduated-
407// intervention cardinal. S/A = GOOD, B = DRIFT, OFF_CANON = BROKEN,
408// UNVERIFIED stays UNVERIFIED-routed-to-DRIFT (ship cautiously).
409
410func nx_dn_tier_from_award(award: nx_int) -> nx_int {
411 if award == NX_DN_AWARD_S_CLASS { return NX_PFT_GOOD }
412 if award == NX_DN_AWARD_A_CLASS { return NX_PFT_GOOD }
413 if award == NX_DN_AWARD_B_CLASS { return NX_PFT_DRIFT }
414 if award == NX_DN_AWARD_OFF_CANON { return NX_PFT_BROKEN }
415 return NX_PFT_DRIFT // UNVERIFIED -> DRIFT (cautious ship)
416}
417
418// ===== Companion-directed builder (Elara as director) =============
419//
420// Convenience: when the companion authors a moment from her own
421// ontological decision (per the user's "external director or
422// one of the companions, elara in this case making ontological or
423// whatever type decisions"), this builder sets the role + source
424// in one call.
425
426func nx_dn_companion_authored(dn: *DirectorsNote,
427 character_id: nx_int,
428 domain: nx_int,
429 affect: nx_int) -> nx_int {
430 dn.director_role = NX_DR_COMPANION
431 dn.source_tag = NX_ST_COMPANION_DECISION
432 dn.character_id = character_id
433 dn.content_domain = domain
434 dn.affect_axis = affect
435 return 0
436}
437
438// ===== External-system builder (batch driver / scheduler) =========
439
440func nx_dn_external_system_authored(dn: *DirectorsNote,
441 arc_id: nx_int,
442 arc_stage: nx_int,
443 moment_seq: nx_int,
444 domain: nx_int) -> nx_int {
445 dn.director_role = NX_DR_EXTERNAL_SYSTEM
446 dn.source_tag = NX_ST_STAGE_POOL
447 dn.arc_id = arc_id
448 dn.arc_stage = arc_stage
449 dn.moment_seq = moment_seq
450 dn.content_domain = domain
451 return 0
452}