nx_content_guard.nx
buildroot/runtime/nx_content_guard.nx
about
nx_content_guard.nx -- extensible content-policy detector.
Runs between scene-index build and caption emit. For each
SceneCandidate, scans the source span for content-policy
signals and writes a flags bitset back into the candidate.
caption_emit checks those flags and refuses CONFIRMED categories,
emits SUSPECT categories with a warning header.
Default categories (V1):
NTR_SUSPECT -- multiple male active referents present
NTR_CONFIRMED -- NTR-coded phrasing detected ("her husband",
"cuckold", "watched him with", etc.)
MULTI_MALE_ACTIVE -- >= 2 distinct male pronoun groups doing verbs
OBSERVER_PASSIVE -- the user/observer pronouns are predominantly
passive verbs (watching, observing); the
user spec is DOMINANT ACTOR not watcher
Future categories (sealed enum extensible append-only):
MINOR_INDICATOR -- age signals below threshold
NONCON_INDICATOR -- non-consent phrasing without resolution
GORE_INDICATOR -- graphic violence
Per cardinal feedback-build-intelligence-never-strip-features:
the guard SURFACES + ATTRIBUTES; it does not silently drop
content. The user reviews flagged candidates and pins / rejects.
Caption emit only refuses CONFIRMED -- everything else is opt-in.
Per cardinal feedback-user-owns-every-bit: thresholds + cue
lists are caller-configurable. No hard-coded magic that the
user can't audit.
nx_safety_envelope:
intended_use: "Scan each SceneCandidate's source span for
content-policy signals (NTR / multi-male-
active / observer-passive / etc.) and
attach flag bits to the candidate."
sil_target: SIL2
asil_target: QM
dal_target: DAL C
iec_62304_class: NONE
dependencies 6 imports · 2 importers
imports: nx_syscalls.nxnx_storybeat.nxnx_storydb.nxnx_media_pool.nxnx_scene_index.nxnx_phrase_match.nx
imported by: nx_content_guard_test.nxnx_ingest_cli.nx
structs
| none |
consts
| 68 | const NX_CG_FLAG_USER_PINNED: i64 = 1 // bit 0 (existing) |
| 69 | const NX_CG_FLAG_NTR_SUSPECT: i64 = 2 // bit 1 |
| 70 | const NX_CG_FLAG_NTR_CONFIRMED: i64 = 4 // bit 2 |
| 71 | const NX_CG_FLAG_MULTI_MALE_ACTIVE: i64 = 8 // bit 3 |
| 72 | const NX_CG_FLAG_OBSERVER_PASSIVE: i64 = 16 // bit 4 |
| 76 | const NX_CG_VERDICT_NONE: i64 = 0 |
| 77 | const NX_CG_VERDICT_SUSPECT: i64 = 1 |
| 78 | const NX_CG_VERDICT_CONFIRMED: i64 = 2 |
| 94 | const NX_CG_NTR_CUE_COUNT: i64 = 14 |
| 137 | const NX_CG_OBS_PASSIVE_CUE_COUNT: i64 = 6 |
| 168 | const NX_CG_MALE_VERB_PAIR_COUNT: i64 = 8 |
functions
| 82 | func nx_cg_load_u8(p: *u8, i: i64) -> i64 |
| 96 | func nx_cg_ntr_cue_at(idx: i64) -> *u8 called by 1: nx_cg_scan_ntr |
| 114 | func nx_cg_ntr_cue_len(idx: i64) -> i64 called by 1: nx_cg_scan_ntr |
| 139 | func nx_cg_obs_passive_cue_at(idx: i64) -> *u8 called by 1: nx_cg_scan_observer_passive |
| 149 | func nx_cg_obs_passive_cue_len(idx: i64) -> i64 called by 1: nx_cg_scan_observer_passive |
| 170 | func nx_cg_male_verb_pair_at(idx: i64) -> *u8 called by 1: nx_cg_scan_male_active |
| 182 | func nx_cg_male_verb_pair_len(idx: i64) -> i64 called by 1: nx_cg_scan_male_active |
| 196 | func nx_cg_scan_ntr(src: *u8, start: i64, end: i64) -> i64 |
| 218 | func nx_cg_scan_male_active(src: *u8, start: i64, end: i64) -> i64 |
| 240 | func nx_cg_scan_observer_passive(src: *u8, start: i64, end: i64) -> i64 |
| 267 | func nx_cg_scan_candidate(db: *StoryDb, cand: *SceneCandidate) -> i64 |
| 312 | func nx_content_guard_scan_bytes(src: *u8, n: i64) -> i64 |
| 333 | func nx_content_guard_scan_index(idx: *SceneIndex, db: *StoryDb) -> i64 |
| 359 | func nx_content_guard_verdict(flags: i64) -> i64 |
| 384 | func nx_content_guard_emit_explain(flags: i64, |