nx_organkind.nx source
↩ module page · 225 lines · 12430 B
1// nx_organkind.nx -- WHAT KIND OF THING IS THIS ORGAN? The classifier seq1492 says both ship verbs need.
2//
3// THE DEFECT IT FIXES: `/api/promote target=nx_torrent_get` -> 400 'daemon/oracle names are never
4// promotable (use /api/deploy)'; `/api/deploy target=nx_torrent_get` -> 400 'unknown target'. Promote
5// calls it a DAEMON, deploy has never heard of it, so a gate-proven binary (nx_poolgov_gate 21/21,
6// deleting a hardcoded MAXP=32) cannot ship by ANY sanctioned route. Both verdicts come from matching
7// NAMES -- a `torrent`/daemon-ish substring -- rather than from what the organ IS.
8// LAW: TWO VERBS THAT DISAGREE ABOUT AN ARTEFACT'S KIND LEAVE IT UNSHIPPABLE -- the classifier, not the
9// allowlist, is the defect.
10//
11// WHY A DECLARED KIND AND NOT A BETTER HEURISTIC: a smarter name rule is still a guess, and the cost of
12// guessing is asymmetric -- misfiling a DAEMON as a one-shot lets promote swap a binary underneath a
13// running process with no health check and no rollback (a rule-26 hazard), while misfiling a one-shot as
14// a daemon merely makes it unshippable. So the kind must be DECLARED DATA (rule 11/17), and anything
15// undeclared must be REFUSED BY BOTH VERBS rather than fall through to a name guess.
16//
17// THE ASYMMETRY IS THE WHOLE POLICY:
18// deploy = for things with a HEALTH SURFACE (a port to probe, so never-brick rollback is meaningful).
19// promote = for one-shots/oracles: atomic rename + .prev backup, no probe needed because nothing is
20// holding the old binary open -- its 'health' is that the NEXT invocation succeeds.
21// A one-shot handed to deploy would have a port-based health check run against a target with no port,
22// making the never-brick verdict meaningless-or-wrong BY CONSTRUCTION.
23//
24// Lives in runtime/ (PRIMITIVE layer, seq1450) so BOTH mgmt handlers can reach it; imports nothing above.
25// license_tier: ORIGINAL Pure + read-only. No hw writes (Rule 26).
26import "nx_syscalls.nx"
27
28const OK_UNKNOWN: i64 = 0
29const OK_DAEMON: i64 = 1
30const OK_ONESHOT: i64 = 2
31const OK_ORACLE: i64 = 3
32const OK_LIB: i64 = 4
33// FIXTURE (2026-09-02): a RUNNABLE WITNESS -- a source with a main that a GATE compiles per run under the
34// declared mode it proves (--chkarith, --optenforce, --ptrprov, --ownership ...) and then asserts the trap or
35// the refusal. A promoted binary of it would prove nothing (the mode flag is the subject, not the program),
36// so BOTH ship verbs refuse it, exactly like a lib. Undeclared, the adoption ladder read every one of them
37// SOURCE-ONLY 'never compiled: /api/build it' and the ranker published DONE rungs as done-but-partially-
38// adopted -- five on /compare/lang alone. The remedy is a DECLARED kind, never a smarter guess (see above).
39const OK_FIXTURE: i64 = 5
40const OK_NL: i64 = 10
41const OK_SP: i64 = 32
42const OK_TAB: i64 = 9
43
44func ok_is_sep(c: i64) -> i64 {
45 if c == OK_SP { return 1 }
46 if c == OK_TAB { return 1 }
47 return 0
48}
49
50func ok_kind_code(buf: *u8, s: i64, e: i64) -> i64 {
51 let n: i64 = e - s
52 if n == 6 { if buf[s] == (100 as u8) { return OK_DAEMON } }
53 if n == 7 { if buf[s] == (111 as u8) { if buf[s+1] == (110 as u8) { return OK_ONESHOT } } }
54 if n == 6 { if buf[s] == (111 as u8) { return OK_ORACLE } }
55 if n == 3 { if buf[s] == (108 as u8) { return OK_LIB } }
56 if n == 7 { if buf[s] == (102 as u8) { return OK_FIXTURE } } // 'fixture': 7 letters, leading f (oneshot is 7, leading o)
57 return OK_UNKNOWN
58}
59
60// Look up <name> in a declared-kind table: one `<name> <kind>` row per line.
61// FAIL-CLOSED: an absent name is OK_UNKNOWN, never a guessed default -- that refusal is the point.
62// Match is WHOLE-FIELD (name must be followed by a separator), so `nx_torrent` can never match
63// `nx_torrent_get` -- the substring lie that produced this defect in the first place.
64func ok_kind_of(buf: *u8, n: i64, name: *u8) -> i64 {
65 var nl: i64 = 0
66 while name[nl] != (0 as u8) { nl = nl + 1 }
67 if nl == 0 { return OK_UNKNOWN }
68 var i: i64 = 0
69 while i < n {
70 var bol: i64 = 0
71 if i == 0 { bol = 1 }
72 if i > 0 { if buf[i-1] == (OK_NL as u8) { bol = 1 } }
73 if bol == 1 {
74 var j: i64 = 0
75 var m: i64 = 1
76 while j < nl {
77 if i + j >= n { m = 0; j = nl } else {
78 if buf[i + j] != name[j] { m = 0; j = nl } else { j = j + 1 }
79 }
80 }
81 if m == 1 {
82 var after: i64 = i + nl
83 var whole: i64 = 0
84 if after < n { if ok_is_sep(buf[after] as i64) == 1 { whole = 1 } }
85 if whole == 1 {
86 // skip the separator run after the name (clean sentinel-free idiom: a flag, not an
87 // out-of-range index -- the k=n+1 / k-1 trick I first wrote collapsed to k=n and made
88 // EVERY lookup return UNKNOWN; the gate caught it at 2/11 before it reached mgmt)
89 var k: i64 = after
90 var r1: i64 = 1
91 while r1 == 1 {
92 if k >= n { r1 = 0 } else {
93 if ok_is_sep(buf[k] as i64) == 1 { k = k + 1 } else { r1 = 0 }
94 }
95 }
96 // the kind token ends at the first separator or newline
97 var kend: i64 = k
98 var r2: i64 = 1
99 while r2 == 1 {
100 if kend >= n { r2 = 0 } else {
101 if ok_is_sep(buf[kend] as i64) == 1 { r2 = 0 } else {
102 if buf[kend] == (OK_NL as u8) { r2 = 0 } else { kend = kend + 1 }
103 }
104 }
105 }
106 return ok_kind_code(buf, k, kend)
107 }
108 }
109 }
110 i = i + 1
111 }
112 return OK_UNKNOWN
113}
114
115const OK_CONFBUF: i64 = 65536
116const OK_CONF_PATH_LEN: i64 = 256
117
118// Self-contained lookup: open the declared-kind table and classify <name>. Returns OK_UNKNOWN when the
119// file is absent or the name is undeclared -- callers then fall back to their legacy policy, so adopting
120// this is ADDITIVE and inert until the conf exists.
121// The file read lives HERE, not in the caller, so a consumer's adoption is ONE LINE. That is deliberate:
122// seq1410 measured that primitives fail to get adopted, and the cost of adoption is the main reason --
123// a primitive that makes its consumer do the plumbing is a primitive that stays dark.
124func ok_kind_of_path(path: *u8, name: *u8) -> i64 {
125 // NO FIXED CAP (2026-08-28). The previous body read at most OK_CONFBUF-1 = 65535 bytes with NO
126 // truncation check, while knowledge/status/organ_kind.conf had reached 62467 -- 3068 bytes of headroom,
127 // 95 pct of the cap consumed, on a file that only ever grows. nx_organkind_gate's T24 states the
128 // consequence exactly: past that cap a DECLARED DAEMON reads as UNDECLARED, and the caller's
129 // terminal-suffix fallback then makes a *_gate name PROMOTABLE -- the one misfile rule 26 forbids.
130 // nx_mgmt_api.nx:3458 is the live ship path that calls this, so the blast radius is /api/promote itself.
131 // MEASURED THE SAME DAY: `nx_wirecensus e` proposes 718 evidence-backed rows totalling 18074 bytes
132 // (/tmp/okemit.txt), which would land the file 15006 bytes PAST the cap -- so the declaration backfill
133 // everybody wants would have silently disarmed both ship verbs on the rows it added, in the flattering
134 // direction, with no error anywhere.
135 // sys_read_file sizes its buffer from the file itself (lseek END) and CANNOT short-read, so the cap is
136 // REMOVED rather than raised: raising it only moves the guess to the next growth spurt.
137 // OK_CONFBUF is deliberately KEPT: nx_organkind_gate's T24 still reads it as its bound, so a revert of
138 // this function re-arms that tooth instead of leaving it dangling.
139 let lp: *i64 = sys_mmap(16) as *i64
140 lp[0] = 0
141 let b: *u8 = sys_read_file(path, lp)
142 let tot: i64 = lp[0]
143 if tot <= 0 { return OK_UNKNOWN }
144 let k: i64 = ok_kind_of(b, tot, name)
145 sys_free_file(b, tot)
146 return k
147}
148
149// deploy is ONLY for things with a health surface to probe.
150func ok_may_deploy(kind: i64) -> i64 {
151 if kind == OK_DAEMON { return 1 }
152 return 0
153}
154
155// promote is for one-shots and oracles: atomic rename + .prev, nothing holds the old binary open.
156// A LIB is neither -- it is compiled INTO consumers, so shipping one means rebuilding them.
157// A FIXTURE is neither -- its gate compiles it per run; a promoted copy would be a binary nobody forks.
158func ok_may_promote(kind: i64) -> i64 {
159 if kind == OK_ONESHOT { return 1 }
160 if kind == OK_ORACLE { return 1 }
161 return 0
162}
163
164// The honest end state: an UNDECLARED organ is refused by BOTH verbs, loudly, instead of being
165// name-guessed into the wrong one. Returns 1 when the kind is declared and at least one verb accepts it.
166func ok_is_shippable(kind: i64) -> i64 {
167 if ok_may_deploy(kind) == 1 { return 1 }
168 if ok_may_promote(kind) == 1 { return 1 }
169 return 0
170}
171
172// ---- seq1789: A TERMINAL SUFFIX IS A DECLARATION, A SUBSTRING IS A GUESS --------------------------
173// THE DEFECT THIS FIXES (measured 2026-07-30): /api/promote refused nx_survey_serve_gate because the
174// legacy shape heuristic md_promote_deny asks `does the name CONTAIN "serve"`. It does -- inside the
175// word `survey_serve` -- so an ORACLE that runs to completion was classified as a long-lived DAEMON and
176// became unshippable by every sanctioned route. Its gate then read exit=127 (binary absent) and that one
177// RED was the only thing holding an otherwise 35/35-grounded, 3/4-green domain off MEASURED-HONEST.
178// LAW: A SUBSTRING CANNOT TELL A ROLE FROM A WORD THAT MERELY APPEARS IN ONE. Position is meaning.
179//
180// WHY THIS IS NOT THE NAME-GUESSING THIS FILE EXISTS TO FORBID. The policy above refuses inference
181// because the cost is ASYMMETRIC: misfiling a DAEMON as a one-shot lets promote swap a binary under a
182// live process with no probe and no rollback (rule 26), while the reverse merely blocks a build. That
183// same asymmetry is what makes a TERMINAL suffix safe where a substring is not:
184// - it is a CONVENTION ENFORCED AT CREATION, not an inference about runtime behaviour;
185// - it is ONE-DIRECTIONAL -- it can only ever yield OK_ORACLE, the SAFE side of the asymmetry, so this
186// rule is INCAPABLE BY CONSTRUCTION of producing the dangerous misfile;
187// - the claim is MECHANICALLY CHECKED, not asserted: nx_organkind_gate proves that no name carrying
188// one of these suffixes can classify as a daemon, and that a DECLARED daemon still wins if one ever
189// did. A guarantee that is only promised is the failure mode this ecosystem keeps paying for.
190// A DECLARED kind always wins -- see ok_kind_or_suffix. This is strictly a floor under UNDECLARED names,
191// which previously fell through to the substring lie.
192func ok_ends_with(name: *u8, sfx: *u8) -> i64 {
193 var nl: i64 = 0
194 while name[nl] != (0 as u8) { nl = nl + 1 }
195 var sl: i64 = 0
196 while sfx[sl] != (0 as u8) { sl = sl + 1 }
197 if sl == 0 { return 0 }
198 if nl < sl { return 0 }
199 var i: i64 = 0
200 var m: i64 = 1
201 while i < sl {
202 if name[nl - sl + i] != sfx[i] { m = 0; i = sl } else { i = i + 1 }
203 }
204 return m
205}
206
207// The oracle-shaped suffixes. Kept in ONE function so the rule and its proof cannot drift apart: the
208// gate exercises this same entry point, so a suffix added here without a daemon-free proof fails there.
209func ok_suffix_kind(name: *u8) -> i64 {
210 if ok_ends_with(name, "_gate" as *u8) == 1 { return OK_ORACLE }
211 if ok_ends_with(name, "_test" as *u8) == 1 { return OK_ORACLE }
212 if ok_ends_with(name, "_kat" as *u8) == 1 { return OK_ORACLE }
213 return OK_UNKNOWN
214}
215
216// THE PRECEDENCE, IN ONE PLACE so no consumer re-implements it (and re-implements it differently, which
217// is how this concern grew two confs and two readers already): a DECLARED kind always wins; only an
218// UNDECLARED name falls back to the terminal-suffix rule; anything else stays UNKNOWN and is refused by
219// both verbs exactly as before. Passing the declared kind IN rather than re-reading the conf keeps this
220// pure and lets the caller pay for the file read once.
221func ok_kind_or_suffix(declared: i64, name: *u8) -> i64 {
222 if declared != OK_UNKNOWN { return declared }
223 return ok_suffix_kind(name)
224}
225