code wiki / _hdl_build / nx_outlink_harvest_gate.nx
nx_outlink_harvest_gate.nx source
↩ module page · 288 lines · 21987 B
1import "nx_gate_gn.nx"
2// nx_outlink_harvest_gate.nx — gate for the shared outlink harvester (P1 PageRank feed). license_tier: ORIGINAL
3// Proves: (T1) olh_scan finds absolute + root-relative <a href> links and rejects assets/fragments; (T2) each
4// edge cid is BYTE-IDENTICAL to ci_hash(resolved_url) — the property that makes an outlink resolve to a real
5// graph node (== dss_urlcid at serve time); (T3) olh_urlok gates content vs junk; (T4) olh_outkey key shape.
6import "nx_outlink_harvest.nx" // olh_scan / olh_urlok / olh_outkey / olh_len ; ci_hash via nx_corpus_ingest
7
8func g_streq(a: *u8, b: *u8) -> i64 {
9 var i: i64 = 0
10 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
11 if b[i] != (0 as u8) { return 0 }
12 return 1
13}
14
15// Canonicalisation scratch for the T1b fragment-collapse tooth. Named rather than a bare 4096: the
16// magic-number ratchet counts a NEW inline literal even when an identical one is grandfathered nearby.
17const OLHG_URLBUF: i64 = 4096
18
19func main() -> i64 {
20 var pass: i64 = 0
21 var fail: i64 = 0
22
23 // --- T1: scan a page with 5 hrefs (abs, root-relative UPPERCASE, en.wikipedia OK, .png, fragment) ---
24 // CHANGED 2026-08-25 FROM 3 TO 4, AND THIS IS A BEHAVIOUR IMPROVEMENT, NOT A TEST BENT TO FIT CODE.
25 // The .png is still rejected (binary extension) -- that half is unchanged and still asserted.
26 // The FRAGMENT link is no longer rejected. olh_urlok refuses any url containing '#', and it used to
27 // see the RAW href, so every fragment-bearing link on the open web was discarded outright. That is
28 // real coverage loss: '#' addresses a position WITHIN a page, so the page itself was always fetchable
29 // and we were throwing it away -- on a frontier measured flat at ~1,880 urls, that matters.
30 // nx_url_canon now strips the fragment BEFORE olh_urlok sees the url, so the page is harvested.
31 // T1b immediately below is what makes this safe rather than merely +1 edge: it proves the fragment
32 // variant canonicalises to the SAME string as the bare url, so N links differing only after '#'
33 // collapse to ONE node instead of becoming N duplicate frontier rows.
34 let base: *u8 = "https://example.org/dir/start" as *u8
35 let html: *u8 = "<p>hi</p><a href='https://example.com/page'>x</a> j <A HREF='/about'>y</a> z <a href='https://en.wikipedia.org/wiki/Foo'>w</a> <a href='https://bad.com/pic.png'>i</a> <a href='https://bad.com/q#f'>f</a>" as *u8
36 let eb: *i64 = sys_mmap(64 * 8) as *i64
37 let n: i64 = olh_scan(html, olh_len(html), base, olh_len(base), eb, 64)
38 if n == 4 { pass = pass + 1 } else { fail = fail + 1; gw("T1 FAIL nedge=" as *u8); gn(n); gw(" (want 4)\n" as *u8) }
39
40 // --- T1b: the fragment variant and the bare url are ONE node. This is the tooth that makes the T1
41 // change above safe: admitting fragment links is only correct if they COLLAPSE, otherwise a page
42 // linked with five different anchors becomes five frontier rows and five documents.
43 let cbf: *u8 = sys_mmap(OLHG_URLBUF)
44 let cbg: *u8 = sys_mmap(OLHG_URLBUF)
45 let frag1: *u8 = "https://bad.com/q#f" as *u8
46 let frag2: *u8 = "https://bad.com/q" as *u8
47 let lf1: i64 = nx_url_canon(frag1, olh_len(frag1), cbf, OLHG_URLBUF - 1)
48 let lf2: i64 = nx_url_canon(frag2, olh_len(frag2), cbg, OLHG_URLBUF - 1)
49 if lf1 == lf2 { if g_streq(cbf, cbg) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T1b FAIL canon differs: " as *u8); gw(cbf); gw(" vs " as *u8); gw(cbg); gw("\n" as *u8) } }
50 else { fail = fail + 1; gw("T1b FAIL lengths differ " as *u8); gn(lf1); gw(" vs " as *u8); gn(lf2); gw("\n" as *u8) }
51
52 // --- T1c: fixture-reached -- the two inputs really ARE different strings, so T1b is proving a
53 // COLLAPSE and not comparing one string with itself.
54 if g_streq(frag1, frag2) == 0 { pass = pass + 1 } else { fail = fail + 1; gw("T1c FAIL fixture is vacuous\n" as *u8) }
55
56 // --- T2: each edge cid == ci_hash(EXACT resolved url) — the node-identity contract ---
57 let u0: *u8 = "https://example.com/page" as *u8
58 let u1: *u8 = "https://example.org/about" as *u8 // root-relative resolved HOST-based (not base+path)
59 let u2: *u8 = "https://en.wikipedia.org/wiki/Foo" as *u8
60 if eb[0] == ci_hash(u0, olh_len(u0)) { pass = pass + 1 } else { fail = fail + 1; gw("T2a FAIL abs-link cid mismatch\n" as *u8) }
61 if eb[1] == ci_hash(u1, olh_len(u1)) { pass = pass + 1 } else { fail = fail + 1; gw("T2b FAIL root-rel got=" as *u8); gn(eb[1]); gw(" want=" as *u8); gn(ci_hash(u1, olh_len(u1))); gw("\n" as *u8) }
62 if eb[2] == ci_hash(u2, olh_len(u2)) { pass = pass + 1 } else { fail = fail + 1; gw("T2c FAIL wiki-link cid mismatch\n" as *u8) }
63 // and all three edge cids are non-negative + distinct (real node ids)
64 if eb[0] >= 0 { if eb[1] >= 0 { if eb[2] >= 0 { pass = pass + 1 } else { fail = fail + 1 } } else { fail = fail + 1 } } else { fail = fail + 1 }
65 if eb[0] != eb[1] { if eb[0] != eb[2] { if eb[1] != eb[2] { pass = pass + 1 } else { fail = fail + 1; gw("T2e dup\n" as *u8) } } else { fail = fail + 1; gw("T2e dup\n" as *u8) } } else { fail = fail + 1; gw("T2e dup\n" as *u8) }
66
67 // --- T3: olh_urlok content filter ---
68 let r1: *u8 = "https://a.com/real-page" as *u8
69 let r2: *u8 = "https://a.com/image.png" as *u8
70 let r3: *u8 = "ftp://a.com/x" as *u8
71 let r4: *u8 = "https://a.com/x?query=1" as *u8
72 let r5: *u8 = "https://de.wikipedia.org/wiki/X" as *u8 // non-en wikipedia -> reject
73 if olh_urlok(r1, olh_len(r1)) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T3a\n" as *u8) }
74 if olh_urlok(r2, olh_len(r2)) == 0 { pass = pass + 1 } else { fail = fail + 1; gw("T3b\n" as *u8) }
75 if olh_urlok(r3, olh_len(r3)) == 0 { pass = pass + 1 } else { fail = fail + 1; gw("T3c\n" as *u8) }
76 if olh_urlok(r4, olh_len(r4)) == 0 { pass = pass + 1 } else { fail = fail + 1; gw("T3d\n" as *u8) }
77 if olh_urlok(r5, olh_len(r5)) == 0 { pass = pass + 1 } else { fail = fail + 1; gw("T3e\n" as *u8) }
78
79 // --- T4: olh_outkey builds "out:<decimal cid>" ---
80 let okey: *u8 = sys_mmap(64)
81 olh_outkey(12345, okey)
82 if g_streq(okey, "out:12345" as *u8) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T4 FAIL key=" as *u8); gw(okey); gw("\n" as *u8) }
83
84 // ============ T8: PATH-CLASS POLICY IS DATA (R11, 2026-08-14) ============
85 // The five /tag/ /category/ literals moved out of olh_urlok_q into knowledge/store/urlpolicy-.
86 // ISOLATED FIXTURE: point the loader at an ABSENT prefix so the DEFAULTS fire deterministically.
87 // Reading the LIVE plane would make this gate report on whatever policy is banked today, which
88 // is a gate sharing its fixture with a production beat.
89 let want_default_rules: i64 = 5 // cardinality of the 2026-08-04 list the defaults reproduce
90 olh_pathpol_set("/tmp/nx_olh_gate_absent/urlpolicy-" as *u8)
91 let q1: *u8 = "https://a.com/tag/asian" as *u8
92 let q2: *u8 = "https://a.com/tags/x" as *u8
93 let q3: *u8 = "https://a.com/category/news" as *u8
94 let q4: *u8 = "https://a.com/tag" as *u8
95 let q5: *u8 = "https://a.com/category" as *u8
96 if olh_path_denied(q1, olh_len(q1)) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T8a FAIL /tag/ admitted\n" as *u8) }
97 if olh_path_denied(q2, olh_len(q2)) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T8b FAIL /tags/ admitted\n" as *u8) }
98 if olh_path_denied(q3, olh_len(q3)) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T8c FAIL /category/ admitted\n" as *u8) }
99 if olh_path_denied(q4, olh_len(q4)) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T8d FAIL endswith /tag admitted\n" as *u8) }
100 if olh_path_denied(q5, olh_len(q5)) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T8e FAIL endswith /category admitted\n" as *u8) }
101 // neg-control-admit: these MUST pass. A judge that refused everything would satisfy every
102 // tooth above and fail ONLY here. A deny-guard with no positive control is blind by construction.
103 let q6: *u8 = "https://a.com/tagline-story" as *u8
104 let q7: *u8 = "https://a.com/2024/03/real-article.html" as *u8
105 if olh_path_denied(q6, olh_len(q6)) == 0 { pass = pass + 1 } else { fail = fail + 1; gw("T8f FAIL neg-control-admit: substring tag refused a non-tag path\n" as *u8) }
106 if olh_path_denied(q7, olh_len(q7)) == 0 { pass = pass + 1 } else { fail = fail + 1; gw("T8g FAIL neg-control-admit: ordinary article refused\n" as *u8) }
107 // ANTI-VACUITY: if sts_load returned nothing AND the defaults never fired, the policy would deny
108 // NOTHING and both neg-controls above would still pass. Bind the rule COUNT, not just verdicts.
109 let nrules: i64 = olh_load_pathpolicy()
110 if nrules >= want_default_rules { pass = pass + 1 } else { fail = fail + 1; gw("T8h FAIL vacuous policy nrules=" as *u8); gn(nrules); gw("\n" as *u8) }
111 // the contract must hold through olh_urlok -- the predicate the five consumers actually call
112 if olh_urlok(q1, olh_len(q1)) == 0 { pass = pass + 1 } else { fail = fail + 1; gw("T8i FAIL olh_urlok admits /tag/\n" as *u8) }
113 if olh_urlok(q7, olh_len(q7)) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T8j FAIL olh_urlok refuses an ordinary article\n" as *u8) }
114 olh_pathpol_set(0 as *u8) // restore production plane for any later tooth
115
116 // --- T6: olh_resolve_root — host-based, byte-checked (the anti-phantom-node contract) ---
117 let rr: *u8 = sys_mmap(8192)
118 let hrefa: *u8 = "/about" as *u8
119 let rl6: i64 = olh_resolve_root("https://example.org/dir/start" as *u8, hrefa, olh_len(hrefa), rr)
120 var t6a: i64 = 0
121 if rl6 == 25 { if g_streq(rr, "https://example.org/about" as *u8) == 1 { t6a = 1 } }
122 if t6a == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T6a FAIL rl=" as *u8); gn(rl6); gw(" got=" as *u8); gw(rr); gw("\n" as *u8) }
123 if olh_resolve_root("no-scheme-base" as *u8, hrefa, olh_len(hrefa), rr) == 0 { pass = pass + 1 } else { fail = fail + 1; gw("T6b FAIL\n" as *u8) }
124
125 // --- T5: empty / no-link html -> zero edges (no spurious rows) ---
126 let h2: *u8 = "<p>no links here at all</p>" as *u8
127 let eb2: *i64 = sys_mmap(8 * 8) as *i64
128 let n2: i64 = olh_scan(h2, olh_len(h2), base, olh_len(base), eb2, 8)
129 if n2 == 0 { pass = pass + 1 } else { fail = fail + 1; gw("T5 FAIL nedge=" as *u8); gn(n2); gw("\n" as *u8) }
130
131 // ================= T7: URL CANONICALISATION (debt 1786031857) =================
132 // Every tooth here has its OPPOSITE somewhere in the block: a pass that only ever proved
133 // "& became &" would also pass if it rewrote every url into the same constant.
134 let cb: *u8 = sys_mmap(4096)
135 let cb2: *u8 = sys_mmap(4096)
136
137 // T7a -- THE MEASURED DEFECT (arxiv fetchfail row, verbatim shape).
138 let raw_a: *u8 = "https://arxiv.org/search/econ?searchtype=author&query=Arslan" as *u8
139 let want_a: *u8 = "https://arxiv.org/search/econ?searchtype=author&query=Arslan" as *u8
140 let ca: i64 = nx_url_canon(raw_a, olh_len(raw_a), cb, 4095)
141 if ca == olh_len(want_a) { if g_streq(cb, want_a) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T7a FAIL got=" as *u8); gw(cb); gw("\n" as *u8) } }
142 else { fail = fail + 1; gw("T7a FAIL len=" as *u8); gn(ca); gw(" want=" as *u8); gn(olh_len(want_a)); gw("\n" as *u8) }
143
144 // T7b -- NON-VACUITY CONTROL: the raw and canonical forms must be DIFFERENT strings, so T7a is
145 // proving a transformation happened and not that the input was already clean.
146 if g_streq(raw_a, want_a) == 0 { pass = pass + 1 } else { fail = fail + 1; gw("T7b FAIL fixture is vacuous\n" as *u8) }
147
148 // T7c -- IDEMPOTENCE. A url is re-harvested on every run; a non-idempotent pass would rewrite the
149 // frontier key each time and the crawler would re-fetch forever.
150 let cc1: i64 = nx_url_canon(cb, ca, cb2, 4095)
151 if cc1 == ca { if g_streq(cb2, cb) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T7c FAIL not idempotent: " as *u8); gw(cb2); gw("\n" as *u8) } }
152 else { fail = fail + 1; gw("T7c FAIL len drift\n" as *u8) }
153
154 // T7d -- THE REGRESSION CONTROL: an ALREADY percent-encoded url must pass through BYTE-IDENTICAL.
155 // Double-encoding '%' would silently break every url we already fetch successfully today.
156 let enc: *u8 = "https://e.org/a%20b?x=%26y&z=1" as *u8
157 let cd: i64 = nx_url_canon(enc, olh_len(enc), cb, 4095)
158 if cd == olh_len(enc) { if g_streq(cb, enc) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T7d FAIL got=" as *u8); gw(cb); gw("\n" as *u8) } }
159 else { fail = fail + 1; gw("T7d FAIL len=" as *u8); gn(cd); gw("\n" as *u8) }
160
161 // T7e -- numeric character references, decimal and hex, both spell '&'.
162 let num1: *u8 = "https://e.org/p?a=1&b=2" as *u8
163 let num2: *u8 = "https://e.org/p?a=1&b=2" as *u8
164 let want_n: *u8 = "https://e.org/p?a=1&b=2" as *u8
165 nx_url_canon(num1, olh_len(num1), cb, 4095)
166 if g_streq(cb, want_n) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T7e1 FAIL got=" as *u8); gw(cb); gw("\n" as *u8) }
167 nx_url_canon(num2, olh_len(num2), cb, 4095)
168 if g_streq(cb, want_n) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T7e2 FAIL got=" as *u8); gw(cb); gw("\n" as *u8) }
169
170 // T7f -- NEVER SWALLOW DATA: an unrecognised reference survives verbatim. A decoder that ate
171 // whatever it did not understand would quietly corrupt urls instead of failing loudly.
172 let unk: *u8 = "https://e.org/p?a=1¬anentity;b=2" as *u8
173 nx_url_canon(unk, olh_len(unk), cb, 4095)
174 if g_streq(cb, unk) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T7f FAIL got=" as *u8); gw(cb); gw("\n" as *u8) }
175 // ...and a bare '&' with no terminator at all is likewise untouched.
176 let bare: *u8 = "https://e.org/p?a=1&b=2" as *u8
177 nx_url_canon(bare, olh_len(bare), cb, 4095)
178 if g_streq(cb, bare) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T7f2 FAIL got=" as *u8); gw(cb); gw("\n" as *u8) }
179
180 // T7g -- pretty-printed HTML splits long hrefs across lines; TAB/LF/CR are REMOVED, not encoded,
181 // and surrounding whitespace is trimmed (a url with a literal newline is an invalid request line).
182 let split: *u8 = " https://e.org/a\n\tb?x=1\r " as *u8
183 let want_s: *u8 = "https://e.org/ab?x=1" as *u8
184 nx_url_canon(split, olh_len(split), cb, 4095)
185 if g_streq(cb, want_s) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T7g FAIL got=" as *u8); gw(cb); gw("\n" as *u8) }
186
187 // T7h -- an INTERIOR space is percent-encoded (not removed): removing it would silently change
188 // which resource is addressed, encoding it is what a browser does.
189 let sp1: *u8 = "https://e.org/a b" as *u8
190 let want_sp: *u8 = "https://e.org/a%20b" as *u8
191 nx_url_canon(sp1, olh_len(sp1), cb, 4095)
192 if g_streq(cb, want_sp) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T7h FAIL got=" as *u8); gw(cb); gw("\n" as *u8) }
193 // non-ascii reference -> percent-encoded UTF-8, never a raw high byte on the wire.
194 let acc: *u8 = "https://e.org/café" as *u8
195 let want_acc: *u8 = "https://e.org/caf%C3%A9" as *u8
196 nx_url_canon(acc, olh_len(acc), cb, 4095)
197 if g_streq(cb, want_acc) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T7h2 FAIL got=" as *u8); gw(cb); gw("\n" as *u8) }
198
199 // T7i -- REFUSE, NEVER TRUNCATE. Given a cap too small, canon returns 0 so the caller SKIPS the
200 // link; a truncated url is a WRONG url, and fetching it would blame the host for our overflow.
201 if nx_url_canon(raw_a, olh_len(raw_a), cb, 8) == 0 { pass = pass + 1 } else { fail = fail + 1; gw("T7i FAIL truncated instead of refusing\n" as *u8) }
202
203 // ---- T7k..T7q: WIRE NORMALISATION (2026-08-25). Every acceptance tooth here is paired with a
204 // NEG-CONTROL, because each of these rules has an over-applying twin that would score perfectly on
205 // the acceptance case alone: upgrade-everything, lowercase-everything, strip-every-port.
206 // T7k -- the MEASURED defect: an http:// link is a guaranteed fetchfail for a TLS-only fetcher, and
207 // its failure poisons the dead-host entry for every working https:// row of the same host.
208 let up1: *u8 = "http://e.org/a" as *u8
209 nx_url_canon(up1, olh_len(up1), cb, 4095)
210 if g_streq(cb, "https://e.org/a" as *u8) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T7k FAIL got=" as *u8); gw(cb); gw("\n" as *u8) }
211
212 // T7l -- NEG-CONTROL: an ALREADY-https url must come back byte-identical. Without this a rule that
213 // rewrote the scheme unconditionally would pass T7k and silently corrupt every other url.
214 let up2: *u8 = "https://e.org/a" as *u8
215 nx_url_canon(up2, olh_len(up2), cb, 4095)
216 if g_streq(cb, up2) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T7l FAIL got=" as *u8); gw(cb); gw("\n" as *u8) }
217
218 // T7m -- HOST is case-insensitive (RFC 3986 6.2.2.1) so it lowercases; the PATH is case-SIGNIFICANT
219 // and must not. One tooth, both halves, because a lowercase-everything rule passes the host half.
220 let cs1: *u8 = "HTTPS://E.ORG/CaseSensitive" as *u8
221 nx_url_canon(cs1, olh_len(cs1), cb, 4095)
222 if g_streq(cb, "https://e.org/CaseSensitive" as *u8) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T7m FAIL got=" as *u8); gw(cb); gw("\n" as *u8) }
223
224 // T7n -- default port removal (RFC 3986 6.2.3): :443 on https, and :80 on an http url that is also
225 // being upgraded (so the two rules are exercised together, which is how they actually occur).
226 let pt1: *u8 = "https://e.org:443/a" as *u8
227 nx_url_canon(pt1, olh_len(pt1), cb, 4095)
228 if g_streq(cb, "https://e.org/a" as *u8) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T7n1 FAIL got=" as *u8); gw(cb); gw("\n" as *u8) }
229 let pt2: *u8 = "http://e.org:80/a" as *u8
230 nx_url_canon(pt2, olh_len(pt2), cb, 4095)
231 if g_streq(cb, "https://e.org/a" as *u8) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T7n2 FAIL got=" as *u8); gw(cb); gw("\n" as *u8) }
232
233 // T7o -- NEG-CONTROL: a NON-default port is content, not noise, and must survive. A strip-every-port
234 // rule would pass both halves of T7n and send every fetch to the wrong service.
235 let pt3: *u8 = "https://e.org:8080/a" as *u8
236 nx_url_canon(pt3, olh_len(pt3), cb, 4095)
237 if g_streq(cb, pt3) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T7o FAIL got=" as *u8); gw(cb); gw("\n" as *u8) }
238
239 // T7p -- fragment dropped: it is resolved by the client and never sent to the origin, so two urls
240 // differing only after it are ONE resource. The fixture is built BYTE-WISE because the nx lexer
241 // forbids '#' inside a string literal -- a fragment url is not writable as one.
242 let frg: *u8 = sys_mmap(128)
243 let fpre: *u8 = "https://e.org/a" as *u8
244 var fi: i64 = 0
245 while fpre[fi] != (0 as u8) { frg[fi] = fpre[fi]; fi = fi + 1 }
246 frg[fi] = 35 as u8; fi = fi + 1
247 let fsuf: *u8 = "section2" as *u8
248 var fj: i64 = 0
249 while fsuf[fj] != (0 as u8) { frg[fi] = fsuf[fj]; fi = fi + 1; fj = fj + 1 }
250 frg[fi] = 0 as u8
251 if fi > olh_len(fpre) { pass = pass + 1 } else { fail = fail + 1; gw("T7p0 FAIL fixture has no fragment to drop\n" as *u8) }
252 nx_url_canon(frg, fi, cb, 4095)
253 if g_streq(cb, fpre) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T7p FAIL got=" as *u8); gw(cb); gw("\n" as *u8) }
254
255 // T7q -- IDEMPOTENCE ACROSS THE NEW RULES. A url is re-harvested every run; if normalisation were
256 // not idempotent the frontier key would change each time and the crawler would re-fetch forever.
257 let idn: i64 = nx_url_canon(up1, olh_len(up1), cb, 4095)
258 let idn2: i64 = nx_url_canon(cb, idn, cb2, 4095)
259 if idn2 == idn { if g_streq(cb2, cb) == 1 { pass = pass + 1 } else { fail = fail + 1; gw("T7q FAIL not idempotent: " as *u8); gw(cb2); gw("\n" as *u8) } }
260 else { fail = fail + 1; gw("T7q FAIL len drift\n" as *u8) }
261
262 // T7j -- THE WHOLE-PATH TOOTH: olh_scan itself must emit the cid of the CLEAN url. The helper
263 // being correct buys nothing if the harvest path does not call it (this estate's adoption gap).
264 // The entity is in the PATH: olh_urlok keeps allowq=0, so a query-string url is (correctly)
265 // rejected from the link graph -- see T7k. A path fixture isolates the canon pass from that policy.
266 let html7: *u8 = "<a href='https://e.org/a&b/c'>x</a>" as *u8
267 let eb7: *i64 = sys_mmap(16 * 8) as *i64
268 let n7: i64 = olh_scan(html7, olh_len(html7), base, olh_len(base), eb7, 16)
269 let clean7: *u8 = "https://e.org/a&b/c" as *u8
270 let dirty7: *u8 = "https://e.org/a&b/c" as *u8
271 if n7 == 1 {
272 if eb7[0] == ci_hash(clean7, olh_len(clean7)) { pass = pass + 1 } else { fail = fail + 1; gw("T7j FAIL edge cid is not the clean url\n" as *u8) }
273 // negative control: it must NOT be the old (dirty) identity, or nothing actually changed.
274 if eb7[0] != ci_hash(dirty7, olh_len(dirty7)) { pass = pass + 1 } else { fail = fail + 1; gw("T7j2 FAIL edge cid still the & form\n" as *u8) }
275 } else { fail = fail + 2; gw("T7j FAIL nedge=" as *u8); gn(n7); gw("\n" as *u8) }
276
277 // T7k -- PIN THE POLICY THAT SURPRISED THIS AUTHOR: the LINK GRAPH still refuses query strings
278 // (olh_urlok == allowq 0), canonical or not. The crawler's frontier uses the trust-aware
279 // wc_urlok instead, which is where curated query urls are admitted. If this tooth ever goes
280 // GREEN-by-change, the link graph and CC ingest have silently stopped agreeing.
281 let htmlq: *u8 = "<a href='https://e.org/p?a=1&b=2'>x</a>" as *u8
282 let ebq: *i64 = sys_mmap(16 * 8) as *i64
283 if olh_scan(htmlq, olh_len(htmlq), base, olh_len(base), ebq, 16) == 0 { pass = pass + 1 } else { fail = fail + 1; gw("T7k FAIL link graph admitted a query url\n" as *u8) }
284
285 gw("outlink-harvest gate: " as *u8); gn(pass); gw(" pass / " as *u8); gn(fail); gw(" fail\n" as *u8)
286 if fail == 0 { gw("OUTLINK-HARVEST GATE GREEN\n" as *u8); return 0 }
287 gw("OUTLINK-HARVEST GATE RED\n" as *u8); return 1
288}