nx_storybeat.nx
buildroot/runtime/nx_storybeat.nx
about
nx_storybeat.nx -- media-agnostic narrative beat.
A StoryBeat is the atomic unit of a parsed narrative across ANY
source medium. The downstream director pipeline (nx_arc /
nx_moment_resolve / nx_directors_note) consumes a stream of
StoryBeats and does not care whether they came from text prose,
a video transcript, a comic-panel image, or a GIF sequence.
Each per-medium ingester (nx_text_ingest, nx_image_ingest,
nx_gif_ingest, nx_video_ingest, nx_audio_ingest) emits beats
into a StoryDb. A beat always points back into the MediaPool
for full-resolution playback / re-extraction.
Schema is FROZEN below this line per cardinal feedback-bug-class-
prevention-additive-not-restrictive: append-only fields, never
renumber or reorder existing ones. Downstream consumers index
by field offset.
See: docs/NISHI_MEDIA_INGESTION_ROADMAP.md
nx_safety_envelope:
intended_use: "Schema and helpers for the atomic narrative
beat unit consumed by the director pipeline."
sil_target: SIL2
asil_target: QM
dal_target: DAL C
iec_62304_class: NONE
evidence: [no_floating_point,
fixed_struct_size_128_bytes,
sealed_enum_beat_kind,
sealed_enum_domain_code,
sealed_enum_consent_tier,
media_id_references_pool_no_aliasing]
hazard_register: [bug-tape-beat-kind-collision,
bug-tape-affect-pack-overflow]
residual_risk: "Affect axes packed i16 each; values outside
[-32768,32767] silently saturate. Caller
uses Q10 fixed-point so saturation is safe."
verdict: NOT_YET_EVALUATED
dependencies 1 imports · 14 importers
diagram shows first 10 each side; +0 more imports, +4 more importers in the complete lists below.
imports: nx_syscalls.nx
imported by: nx_arc_anchor_test.nxnx_caption_emit.nxnx_caption_emit_test.nxnx_content_guard.nxnx_content_guard_test.nxnx_ingest_cli.nxnx_scene_index.nxnx_scene_index_test.nxnx_storydb.nxnx_term_cooccur.nxnx_term_extract.nxnx_term_extract_test.nxnx_text_ingest.nxnx_text_ingest_test.nx
structs
| 119 | struct StoryBeat |
consts
| 48 | const NX_BEAT_UNKNOWN: i64 = 0 |
| 49 | const NX_BEAT_NARRATION: i64 = 1 // 3rd-person prose narration |
| 50 | const NX_BEAT_DIALOGUE: i64 = 2 // quoted speech |
| 51 | const NX_BEAT_ACTION: i64 = 3 // verb-led action sentence |
| 52 | const NX_BEAT_DESCRIPTION: i64 = 4 // setting / appearance description |
| 53 | const NX_BEAT_TRANSITION: i64 = 5 // time / scene break ("Hours later") |
| 54 | const NX_BEAT_HEADING: i64 = 6 // chapter / scene header line |
| 55 | const NX_BEAT_MEDIA_FRAME: i64 = 7 // single frame from video / gif / image |
| 56 | const NX_BEAT_MEDIA_CAPTION: i64 = 8 // ASR / OCR caption from audio / image |
| 57 | const NX_BEAT_INTERIORITY: i64 = 9 // 1st-person inner thought (italics) |
| 58 | const NX_BEAT_GESTURE: i64 = 10 // non-verbal body / face expression |
| 65 | const NX_DOMAIN_UNKNOWN: i64 = 0 |
| 66 | const NX_DOMAIN_MUNDANE: i64 = 1 // breakfast, work, errands |
| 67 | const NX_DOMAIN_INTIMATE: i64 = 2 // kiss / hug / cuddle, no explicit |
| 68 | const NX_DOMAIN_EROTIC: i64 = 3 // sexual content, consenting adults |
| 69 | const NX_DOMAIN_FANTASY: i64 = 4 // fantasy / sci-fi setting tag |
| 70 | const NX_DOMAIN_VIOLENCE: i64 = 5 // physical violence (combat / injury) |
| 78 | const NX_CONSENT_UNKNOWN: i64 = 0 |
| 79 | const NX_CONSENT_PUBLIC: i64 = 1 // public-domain / CC0 |
| 80 | const NX_CONSENT_PRIVATE_OK: i64 = 2 // user-owned, private use only |
| 81 | const NX_CONSENT_EROTIC_OK: i64 = 3 // user explicitly tagged for erotic |
| 82 | const NX_CONSENT_EXPLICIT: i64 = 4 // explicit user authorization granted |
| 90 | const NX_ARCSTAGE_UNKNOWN: i64 = 0 |
| 91 | const NX_ARCSTAGE_SETUP: i64 = 1 |
| 92 | const NX_ARCSTAGE_HOOK: i64 = 2 |
| 93 | const NX_ARCSTAGE_TRIGGER: i64 = 3 |
| 94 | const NX_ARCSTAGE_ENGAGE: i64 = 4 |
| 95 | const NX_ARCSTAGE_RISING: i64 = 5 |
| 96 | const NX_ARCSTAGE_OBSTACLE: i64 = 6 |
| 97 | const NX_ARCSTAGE_TURN: i64 = 7 |
| 98 | const NX_ARCSTAGE_DEEPEN: i64 = 8 |
| 99 | const NX_ARCSTAGE_BUILD: i64 = 9 |
| 100 | const NX_ARCSTAGE_PEAK: i64 = 10 |
| 101 | const NX_ARCSTAGE_CLIMAX: i64 = 11 |
| 102 | const NX_ARCSTAGE_BREATH: i64 = 12 |
| 103 | const NX_ARCSTAGE_AFTER: i64 = 13 |
| 104 | const NX_ARCSTAGE_RESOLVE: i64 = 14 |
| 105 | const NX_ARCSTAGE_COOL: i64 = 15 |
| 109 | const NX_BEAT_FLAG_IS_QUOTED: i64 = 1 // text was inside "..." |
| 110 | const NX_BEAT_FLAG_IS_ITALIC: i64 = 2 // interiority styling |
| 111 | const NX_BEAT_FLAG_HAS_SUMMARY: i64 = 4 // summary_off/len valid |
| 112 | const NX_BEAT_FLAG_USER_PINNED: i64 = 8 // user starred this beat |
| 113 | const NX_BEAT_FLAG_DIRECTOR_CUE: i64 = 16 // marked for director init |
| 150 | const NX_STORYBEAT_BYTES: i64 = 128 |
functions
| 163 | func nx_beat_pack_affect(arousal: i64, tenderness: i64, |
| 172 | func nx_beat_affect_arousal(packed: i64) -> i64 called by 1: main |
| 178 | func nx_beat_affect_tenderness(packed: i64) -> i64 called by 1: main |
| 184 | func nx_beat_affect_urgency(packed: i64) -> i64 called by 1: main |
| 190 | func nx_beat_affect_intimacy(packed: i64) -> i64 called by 1: main |
| 198 | func nx_beat_init(b: *StoryBeat, beat_kind: i64, media_type: i64, called by 1: nx_storydb_push_beat |
| 218 | func nx_beat_set_affect(b: *StoryBeat, arousal: i64, tenderness: i64, calls 1: nx_beat_pack_affect |
| 224 | func nx_beat_set_summary(b: *StoryBeat, off: i64, len: i64) |
| 230 | func nx_beat_mark_director_cue(b: *StoryBeat) |
| 234 | func nx_beat_has_director_cue(b: *StoryBeat) -> i64 |