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 // "&amp; 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&amp;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&#38;b=2" as *u8 163 let num2: *u8 = "https://e.org/p?a=1&#x26;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&notanentity;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&#233;" 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&amp;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&amp;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 &amp; 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&amp;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}