code wiki / _hdl_build / nx_web_crawl_step_gate.nx
nx_web_crawl_step_gate.nx source
↩ module page · 408 lines · 24415 B
1// nx_web_crawl_step_gate.nx -- network-free KATs for the frontier-persisted crawler's STORE mechanics:
2// the url filter, fr: row write/enumerate, the P->D state flip (append-only last-wins), cross-segment
3// dedupe, and unknown-state. The fetch/ingest spine is nx_web_ingest's, already proven on the real web.
4// Runs against a /tmp shard (wiped per boot). license_tier: ORIGINAL
5import "nx_web_crawl_step.nx"
6import "nx_gate_verdict.nx"
7
8func gw_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
9func gw_num(v: i64) -> i64 {
10 let bb: *u8 = sys_mmap(28); var m: i64 = v
11 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
12 let t: *u8 = sys_mmap(28); var k: i64 = 0
13 if m == 0 { t[0] = 48 as u8; k = 1 }
14 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
15 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
16 sys_write(1, bb, k); return 0
17}
18func gw_check(name: *u8, cond: i64, pass: *i64, total: *i64) -> i64 {
19 total[0] = total[0] + 1
20 gw_puts(name)
21 if cond == 1 { pass[0] = pass[0] + 1; gw_puts(" PASS\n" as *u8) } else { gw_puts(" FAIL\n" as *u8) }
22 return 0
23}
24func gw_ok(u: *u8) -> i64 { return wc_urlok(u, wc_len(u)) }
25
26func main() -> i64 {
27 gw_puts("=== nx_web_crawl_step gate (frontier store mechanics, network-free) ===\n" as *u8)
28 let pass: *i64 = sys_mmap(16) as *i64; pass[0] = 0
29 let total: *i64 = sys_mmap(16) as *i64; total[0] = 0
30
31 // R10: establish the query policy ONCE, up front, so EVERY tooth below runs under a known,
32 // self-contained deny list rather than whatever the live urlpolicy- plane happens to hold.
33 // 'action' earns its place next to 'utm_source': ?action=edit|raw|history is a MediaWiki ACTION
34 // ENDPOINT, not a document. R8 killed it for free via the blanket '?' reject; once queries are
35 // judged by shape, that free kill has to be paid for explicitly -- and T1 is what noticed.
36 // ★REMOVING A BLANKET RULE BILLS YOU FOR EVERY CASE IT WAS SILENTLY COVERING.
37 let gqd: *u8 = sys_mmap(32 * 8)
38 var gqi: i64 = 0
39 let gqd0: *u8 = "utm_source" as *u8
40 while gqd0[gqi] != (0 as u8) { gqd[gqi] = gqd0[gqi]; gqi = gqi + 1 }
41 gqd[gqi] = 0 as u8
42 let gqd1: *u8 = "action" as *u8
43 var gqj: i64 = 0
44 while gqd1[gqj] != (0 as u8) { gqd[32 + gqj] = gqd1[gqj]; gqj = gqj + 1 }
45 gqd[32 + gqj] = 0 as u8
46 wc_qdeny_g = gqd
47 wc_nqdeny_g = 2
48
49 // T1 url filter: keeps article urls, kills queries/fragments/meta/binaries
50 var f1: i64 = 1
51 if gw_ok("https://en.wikipedia.org/wiki/Chess" as *u8) != 1 { f1 = 0 }
52 if gw_ok("https://example.org/a/deep/page" as *u8) != 1 { f1 = 0 }
53 if gw_ok("https://en.wikipedia.org/w/index.php?action=edit" as *u8) != 0 { f1 = 0 }
54 if gw_ok("https://en.wikipedia.org/wiki/Chess#History" as *u8) != 0 { f1 = 0 }
55 if gw_ok("https://en.wikipedia.org/wiki/Special:Random" as *u8) != 0 { f1 = 0 }
56 if gw_ok("https://en.wikipedia.org/wiki/File:Board.png" as *u8) != 0 { f1 = 0 }
57 if gw_ok("https://x.org/logo.png" as *u8) != 0 { f1 = 0 }
58 if gw_ok("https://x.org/style.css" as *u8) != 0 { f1 = 0 }
59 if gw_ok("mailto:someone@example.org" as *u8) != 0 { f1 = 0 }
60 if gw_ok("/relative/only" as *u8) != 0 { f1 = 0 }
61 if gw_ok("https://ro.wikipedia.org/wiki/Literatura" as *u8) != 0 { f1 = 0 } // interwiki lang edition
62 if gw_ok("https://en.wikipedia.org/wiki/Chess_opening" as *u8) != 1 { f1 = 0 } // en stays
63 if gw_ok("https://commons.wikimedia.org/wiki/X" as *u8) != 0 { f1 = 0 }
64 // API / feed endpoints = real bytes but not content -> rejected
65 if gw_ok("https://creativecommons.org/wp-json/" as *u8) != 0 { f1 = 0 }
66 if gw_ok("https://site.com/api/data.json" as *u8) != 0 { f1 = 0 }
67 if gw_ok("https://blog.com/feed" as *u8) != 0 { f1 = 0 }
68 if gw_ok("https://blog.com/comments/feed/" as *u8) != 0 { f1 = 0 }
69 if gw_ok("https://site.com/articles/a-real-story" as *u8) != 1 { f1 = 0 } // content still passes
70 // anti-slop listing classes (2026-08-04): tag/category link farms refused at admission
71 if gw_ok("https://healthquerys.com/tag/puriva-france/" as *u8) != 0 { f1 = 0 }
72 if gw_ok("https://blog.com/category/wellness/" as *u8) != 0 { f1 = 0 }
73 if gw_ok("https://blog.com/posts/2024/tagged-story" as *u8) != 1 { f1 = 0 } // 'tag' inside a word still passes
74 gw_check("T1 url filter (21 KATs incl. interwiki + API/feed + tag/category farms)" as *u8, f1, pass, total)
75
76 // FIXTURE SELF-CLEAN (2026-08-04, debt 1785882035): /tmp persists across runs on the NAS, so a
77 // previous run's segments made the count teeth (T2-T4) go 4/7 RED on any second invocation -- a
78 // gate that is only green on a fresh boot is a boot-witness, not a gate. Unlink the whole fixture.
79 sys_unlinkat("/tmp/crawlgate-manifest.txt" as *u8)
80 let cleanbuf: *u8 = sys_mmap(96)
81 let cdig: *u8 = sys_mmap(28)
82 var ci0: i64 = 1
83 while ci0 <= 32 {
84 var ce: i64 = 0
85 while ce < 5 {
86 var ext: *u8 = ".docs" as *u8
87 if ce == 1 { ext = ".idx" as *u8 }
88 if ce == 2 { ext = ".pos" as *u8 }
89 if ce == 3 { ext = ".imp" as *u8 }
90 if ce == 4 { ext = ".terms" as *u8 }
91 var co: i64 = 0
92 let cpre: *u8 = "/tmp/crawlgate-seg-" as *u8
93 while cpre[co] != (0 as u8) { cleanbuf[co] = cpre[co]; co = co + 1 }
94 var cm: i64 = ci0
95 var ck: i64 = 0
96 while cm > 0 { cdig[ck] = (48 + (cm % 10)) as u8; cm = cm / 10; ck = ck + 1 }
97 var cj: i64 = 0
98 while cj < ck { cleanbuf[co] = cdig[ck - 1 - cj]; co = co + 1; cj = cj + 1 }
99 var cx: i64 = 0
100 while ext[cx] != (0 as u8) { cleanbuf[co] = ext[cx]; co = co + 1; cx = cx + 1 }
101 cleanbuf[co] = 0 as u8
102 sys_unlinkat(cleanbuf)
103 ce = ce + 1
104 }
105 ci0 = ci0 + 1
106 }
107
108 // build a tiny /tmp frontier shard
109 let prefix: *u8 = "/tmp/crawlgate-" as *u8
110 let segidbox: *i64 = sys_mmap(16) as *i64; segidbox[0] = 1
111 let segsbox: *i64 = sys_mmap(16) as *i64; segsbox[0] = 0
112 let w: *i64 = ss_begin()
113 let u1: *u8 = "https://a.example/one" as *u8
114 let u2: *u8 = "https://a.example/two" as *u8
115 let u3: *u8 = "https://a.example/three" as *u8
116 wc_frmark(w, prefix, segidbox, segsbox, u1, wc_len(u1), 1)
117 wc_frmark(w, prefix, segidbox, segsbox, u2, wc_len(u2), 1)
118 wc_frmark(w, prefix, segidbox, segsbox, u3, wc_len(u3), 2) // three = already done
119 ss_commit(prefix, w, segidbox[0])
120
121 // T2 enumerate: exactly the two P urls
122 let h1: *i64 = ss_open(prefix)
123 let urls: *u8 = sys_mmap(2048 * 16)
124 let canh0: *u8 = sys_mmap(64 * 8) // R4: never read when canonly=0 -- T2/T3/T4 keep the pre-R4 contract
125 let n2: i64 = wc_pending(h1, prefix, urls, 16, canh0, 0, 0)
126 gw_check("T2 pending enumeration -> 2 (P rows only)" as *u8, (n2 == 2) as i64, pass, total)
127
128 // T3 state flip: mark one P->D in a NEW segment; enumeration drops to 1 (last-wins across segments)
129 segidbox[0] = segidbox[0] + 1
130 let w2: *i64 = ss_begin()
131 wc_frmark(w2, prefix, segidbox, segsbox, u1, wc_len(u1), 2)
132 ss_commit(prefix, w2, segidbox[0])
133 let h2: *i64 = ss_open(prefix)
134 let n3: i64 = wc_pending(h2, prefix, urls, 16, canh0, 0, 0)
135 var t3: i64 = 0
136 if n3 == 1 {
137 // the survivor must be u2
138 var eq: i64 = 1
139 var x: i64 = 0
140 var go: i64 = 1
141 while go == 1 { if urls[x] != u2[x] { eq = 0; go = 0 } else { if u2[x] == (0 as u8) { go = 0 } else { x = x + 1 } } }
142 if eq == 1 { t3 = 1 }
143 }
144 gw_check("T3 P->D flip (append-only, last-wins) -> 1 pending = /two" as *u8, t3, pass, total)
145
146 // T4 duplicate P rows across segments dedupe to one pending
147 segidbox[0] = segidbox[0] + 1
148 let w3: *i64 = ss_begin()
149 wc_frmark(w3, prefix, segidbox, segsbox, u2, wc_len(u2), 1) // re-bank the SAME pending url
150 ss_commit(prefix, w3, segidbox[0])
151 let h3: *i64 = ss_open(prefix)
152 let n4: i64 = wc_pending(h3, prefix, urls, 16, canh0, 0, 0)
153 gw_check("T4 dup P rows across segments dedupe -> still 1" as *u8, (n4 == 1) as i64, pass, total)
154
155 // T5 unknown state for a never-seen url
156 let s5: i64 = wc_frstate(h3, "https://a.example/never" as *u8, 23)
157 gw_check("T5 unknown url -> state 0" as *u8, (s5 == 0) as i64, pass, total)
158 // T5b known states read back
159 var t5b: i64 = 0
160 if wc_frstate(h3, u2, wc_len(u2)) == 1 { if wc_frstate(h3, u1, wc_len(u1)) == 2 { t5b = 1 } }
161 gw_check("T5b states read back (two=P, one=D)" as *u8, t5b, pass, total)
162
163 // T6 (P1 LINK GRAPH): wc_harvest captures ALL of a page's outlinks as out:<cid(base)> = packed i64 target
164 // cid(url)s -- the edge list PageRank runs on. Distinguishing by construction: before this change the
165 // crawler stored NO edges, so out:<cid> is absent and t6=0.
166 let t1u: *u8 = "https://tgt1.example/story-alpha" as *u8
167 let t2u: *u8 = "https://tgt2.example/story-beta" as *u8
168 let hbase: *u8 = "https://src.example/page-x" as *u8
169 let html6: *u8 = "<html><body><a href='https://tgt1.example/story-alpha'>A</a> <a href='https://tgt2.example/story-beta'>B</a></body></html>" as *u8
170 let hdisc: *i64 = sys_mmap(16) as *i64; hdisc[0] = 0
171 let hrs: *u8 = sys_mmap(256 * 64); let hnrs: *i64 = sys_mmap(16) as *i64; hnrs[0] = 0
172 segidbox[0] = segidbox[0] + 1
173 let w6: *i64 = ss_begin()
174 wc_harvest(html6, wc_len(html6), hbase, wc_len(hbase), 0 as *i64, w6, prefix, segidbox, segsbox, hdisc, hrs, hnrs)
175 ss_commit(prefix, w6, segidbox[0])
176 let h6: *i64 = ss_open(prefix)
177 let okey6: *u8 = sys_mmap(64)
178 wc_outkey(ci_hash(hbase, wc_len(hbase)), okey6)
179 let op6: *i64 = sys_mmap(16) as *i64
180 let ol6: *i64 = sys_mmap(16) as *i64
181 var t6: i64 = 0
182 if ss_hget(h6, okey6, op6, ol6) == 1 { if ol6[0] == 16 {
183 let ev: *i64 = op6[0] as *i64
184 if ev[0] == ci_hash(t1u, wc_len(t1u)) { if ev[1] == ci_hash(t2u, wc_len(t2u)) { t6 = 1 } }
185 } }
186 gw_check("T6 P1 outlink capture (out:<cid(base)> = [cid(tgt1),cid(tgt2)], ordered)" as *u8, t6, pass, total)
187
188 // T7 (R3 host-health): hd:<hosthash> streak round-trip; an unknown host reads 0
189 segidbox[0] = segidbox[0] + 1
190 let w7: *i64 = ss_begin()
191 let hhA: i64 = wc_hosthash("dead.example" as *u8, 12)
192 wc_hd_put(w7, prefix, segidbox, segsbox, hhA, 3)
193 ss_commit(prefix, w7, segidbox[0])
194 let h7: *i64 = ss_open(prefix)
195 var t7: i64 = 0
196 if wc_hd_streak(h7, hhA) == 3 { if wc_hd_streak(h7, wc_hosthash("live.example" as *u8, 12)) == 0 { t7 = 1 } }
197 gw_check("T7 hd: streak round-trip (3) + unknown-host 0" as *u8, t7, pass, total)
198
199 // T8 (R3): retire-if-rotten -- streak>=WC_HD_RETIRE retires a P row (P->D visible after commit),
200 // streak below the bar keeps it pending, and a SEED url NEVER retires even on a rotten host
201 // (wc_seed_add is blocked by any existing fr: row, so a retired seed would be permanently dead).
202 let ur1: *u8 = "https://dead.example/rot1" as *u8
203 let ur2: *u8 = "https://dying.example/fresh" as *u8
204 let ur3: *u8 = "https://dead.example/seedhub" as *u8
205 segidbox[0] = segidbox[0] + 1
206 let w8: *i64 = ss_begin()
207 wc_frmark(w8, prefix, segidbox, segsbox, ur1, wc_len(ur1), 1)
208 wc_frmark(w8, prefix, segidbox, segsbox, ur2, wc_len(ur2), 1)
209 wc_frmark(w8, prefix, segidbox, segsbox, ur3, wc_len(ur3), 1)
210 let hhB: i64 = wc_hosthash("dying.example" as *u8, 13)
211 wc_hd_put(w8, prefix, segidbox, segsbox, hhB, 2)
212 ss_commit(prefix, w8, segidbox[0])
213 let h8: *i64 = ss_open(prefix)
214 let seeds8: *u8 = sys_mmap(WC_MAGIC_2048)
215 var so8: i64 = 0
216 while ur3[so8] != (0 as u8) { seeds8[so8] = ur3[so8]; so8 = so8 + 1 }
217 seeds8[so8] = 0 as u8
218 segidbox[0] = segidbox[0] + 1
219 let w9: *i64 = ss_begin()
220 // the caller pattern: seed membership guards the retire (exactly what main's skip branch does)
221 var r1: i64 = 0
222 if wc_is_seed(seeds8, 1, ur1, wc_len(ur1)) == 0 { r1 = wc_retire_if_rotten(h8, w9, prefix, segidbox, segsbox, ur1, wc_len(ur1), hhA) }
223 var r2: i64 = 0
224 if wc_is_seed(seeds8, 1, ur2, wc_len(ur2)) == 0 { r2 = wc_retire_if_rotten(h8, w9, prefix, segidbox, segsbox, ur2, wc_len(ur2), hhB) }
225 var r3: i64 = 0
226 if wc_is_seed(seeds8, 1, ur3, wc_len(ur3)) == 0 { r3 = wc_retire_if_rotten(h8, w9, prefix, segidbox, segsbox, ur3, wc_len(ur3), hhA) }
227 if w9[1] > 0 { ss_commit(prefix, w9, segidbox[0]) }
228 let h9: *i64 = ss_open(prefix)
229 var t8: i64 = 0
230 if r1 == 1 { if r2 == 0 { if r3 == 0 {
231 if wc_frstate(h9, ur1, wc_len(ur1)) == 2 { if wc_frstate(h9, ur2, wc_len(ur2)) == 1 { if wc_frstate(h9, ur3, wc_len(ur3)) == 1 { t8 = 1 } } }
232 } } }
233 gw_check("T8 retire-if-rotten: streak>=3 retires, <3 keeps, seed exempt" as *u8, t8, pass, total)
234
235 // ---- T9 (R4 CANONICAL LANE) ---------------------------------------------------------------
236 // The host-filtered pull must keep ONLY canonical-seed hosts, match a SUBDOMAIN on the boundary
237 // dot, and REFUSE a lookalike host. Network-free -- store rows only. The pre-existing fixture
238 // rows (a.example / b.example) are non-canonical by construction, so they are excluded too and
239 // the count is stable regardless of what T1-T8 left pending.
240 segidbox[0] = segidbox[0] + 1
241 let q9w: *i64 = ss_begin()
242 let q9c1: *u8 = "https://en.wikipedia.org/wiki/Hypertension" as *u8
243 let q9c2: *u8 = "https://data.who.int/topic/air" as *u8
244 let q9c3: *u8 = "https://notwikipedia.org/lookalike" as *u8
245 let q9c4: *u8 = "https://junkfarm.example/tag/a" as *u8
246 wc_frmark(q9w, prefix, segidbox, segsbox, q9c1, wc_len(q9c1), 1)
247 wc_frmark(q9w, prefix, segidbox, segsbox, q9c2, wc_len(q9c2), 1)
248 wc_frmark(q9w, prefix, segidbox, segsbox, q9c3, wc_len(q9c3), 1)
249 wc_frmark(q9w, prefix, segidbox, segsbox, q9c4, wc_len(q9c4), 1)
250 ss_commit(prefix, q9w, segidbox[0])
251 let q9seeds: *u8 = sys_mmap(WC_MAGIC_2048 * 4)
252 let q9s1: *u8 = "https://www.who.int/" as *u8
253 var q9a: i64 = 0
254 while q9s1[q9a] != (0 as u8) { q9seeds[q9a] = q9s1[q9a]; q9a = q9a + 1 }
255 q9seeds[q9a] = 0 as u8
256 let q9d2: *u8 = (q9seeds as i64 + WC_MAGIC_2048) as *u8
257 let q9s2: *u8 = "https://wikipedia.org/" as *u8
258 var q9b: i64 = 0
259 while q9s2[q9b] != (0 as u8) { q9d2[q9b] = q9s2[q9b]; q9b = q9b + 1 }
260 q9d2[q9b] = 0 as u8
261 let q9canh: *u8 = sys_mmap(64 * WC_MAXCANH)
262 let q9ncan: i64 = wc_canon_hosts(q9seeds, 2, q9canh)
263 let q9h: *i64 = ss_open(prefix)
264 let q9urls: *u8 = sys_mmap(WC_MAGIC_2048 * 32)
265 let q9n: i64 = wc_pending(q9h, prefix, q9urls, 32, q9canh, q9ncan, 1)
266 var t9: i64 = 0
267 if q9ncan == 2 { if wc_streq(q9canh, "who.int" as *u8) == 1 {
268 if wc_streq((q9canh as i64 + 64) as *u8, "wikipedia.org" as *u8) == 1 { if q9n == 2 { t9 = 1 } }
269 } }
270 gw_check("T9 canonical lane: www-stripped hosts, subdomain kept, junk excluded -> 2 of 4" as *u8, t9, pass, total)
271 // T9b THE NEGATIVE CONTROL that makes T9 non-vacuous: a lookalike host must NOT ride the lane.
272 let q9hb: *u8 = sys_mmap(64)
273 var t9b: i64 = 0
274 if wc_host_is_canon(q9canh, q9ncan, q9c1, q9hb) == 1 { if wc_host_is_canon(q9canh, q9ncan, q9c2, q9hb) == 1 {
275 if wc_host_is_canon(q9canh, q9ncan, q9c3, q9hb) == 0 { if wc_host_is_canon(q9canh, q9ncan, q9c4, q9hb) == 0 { t9b = 1 } }
276 } }
277 gw_check("T9b lookalike REFUSED: notwikipedia.org needs the boundary dot to match wikipedia.org" as *u8, t9b, pass, total)
278 // T9c canonly=0 is the pre-R4 contract: the SAME walk sees all four rows (plus fixture leftovers).
279 let q9all: i64 = wc_pending(q9h, prefix, q9urls, 32, q9canh, 0, 0)
280 gw_check("T9c canonly=0 unchanged: unfiltered walk still sees strictly more rows than the lane" as *u8, (q9all > q9n) as i64, pass, total)
281 // T9d (R4b): the host-string matcher is the ONE rule and the url matcher only wraps it. If these
282 // two ever disagree, the canonical LANE and the canonical DEPTH CAP would disagree about which
283 // hosts are curated -- a split definition is exactly how a privilege leaks to a lookalike host.
284 var q9agree: i64 = 1
285 if wc_host_str_is_canon(q9canh, q9ncan, "wikipedia.org" as *u8, 13) != 1 { q9agree = 0 }
286 if wc_host_str_is_canon(q9canh, q9ncan, "en.wikipedia.org" as *u8, 16) != 1 { q9agree = 0 }
287 if wc_host_str_is_canon(q9canh, q9ncan, "data.who.int" as *u8, 12) != 1 { q9agree = 0 }
288 if wc_host_str_is_canon(q9canh, q9ncan, "notwikipedia.org" as *u8, 16) != 0 { q9agree = 0 }
289 if wc_host_str_is_canon(q9canh, q9ncan, "junkfarm.example" as *u8, 16) != 0 { q9agree = 0 }
290 gw_check("T9d host-string matcher == url matcher (exact, subdomain, lookalike, junk)" as *u8, q9agree, pass, total)
291
292 // ---- T10 (R5 SEED FRESHNESS RE-QUEUE) -------------------------------------------------------
293 // A D seed must return to P and be copied into the lane buffer; a P seed and a never-seen seed
294 // must be left alone. Network-free, store rows only.
295 segidbox[0] = segidbox[0] + 1
296 let qAw: *i64 = ss_begin()
297 let qA1: *u8 = "https://canon-a.example/hub" as *u8
298 let qA2: *u8 = "https://canon-b.example/hub" as *u8
299 let qA3: *u8 = "https://canon-c.example/hub" as *u8
300 wc_frmark(qAw, prefix, segidbox, segsbox, qA1, wc_len(qA1), 2)
301 wc_frmark(qAw, prefix, segidbox, segsbox, qA2, wc_len(qA2), 1)
302 ss_commit(prefix, qAw, segidbox[0])
303 let qAseeds: *u8 = sys_mmap(WC_MAGIC_2048 * 4)
304 var qAi: i64 = 0
305 while qA1[qAi] != (0 as u8) { qAseeds[qAi] = qA1[qAi]; qAi = qAi + 1 }
306 qAseeds[qAi] = 0 as u8
307 let qAd2: *u8 = (qAseeds as i64 + WC_MAGIC_2048) as *u8
308 qAi = 0
309 while qA2[qAi] != (0 as u8) { qAd2[qAi] = qA2[qAi]; qAi = qAi + 1 }
310 qAd2[qAi] = 0 as u8
311 let qAd3: *u8 = (qAseeds as i64 + 2 * WC_MAGIC_2048) as *u8
312 qAi = 0
313 while qA3[qAi] != (0 as u8) { qAd3[qAi] = qA3[qAi]; qAi = qAi + 1 }
314 qAd3[qAi] = 0 as u8
315 let qAh: *i64 = ss_open(prefix)
316 segidbox[0] = segidbox[0] + 1
317 let qAw2: *i64 = ss_begin()
318 let qAdst: *u8 = sys_mmap(WC_MAGIC_2048 * 8)
319 let qAbox: *i64 = sys_mmap(16) as *i64
320 qAbox[0] = 0
321 qAbox[1] = 6
322 let qArq: i64 = wc_seed_refresh(qAh, qAw2, prefix, segidbox, segsbox, qAseeds, 3, qAdst, qAbox)
323 if qAw2[1] > 0 { ss_commit(prefix, qAw2, segidbox[0]) }
324 let qAh2: *i64 = ss_open(prefix)
325 var t10: i64 = 0
326 if qArq == 1 { if qAbox[0] == 1 {
327 if wc_frstate(qAh2, qA1, wc_len(qA1)) == 1 { if wc_frstate(qAh2, qA2, wc_len(qA2)) == 1 { if wc_frstate(qAh2, qA3, wc_len(qA3)) == 0 {
328 if wc_streq(qAdst, qA1) == 1 { t10 = 1 }
329 } } }
330 } }
331 gw_check("T10 seed re-queue: D seed -> P + copied to lane; P and never-seen UNTOUCHED" as *u8, t10, pass, total)
332 // T10b the persisted cursor round-trips (the rotation mechanism across runs)
333 segidbox[0] = segidbox[0] + 1
334 let qAw3: *i64 = ss_begin()
335 wc_sr_put(qAw3, prefix, segidbox, segsbox, 5)
336 if qAw3[1] > 0 { ss_commit(prefix, qAw3, segidbox[0]) }
337 let qAh3: *i64 = ss_open(prefix)
338 gw_check("T10b sr:cursor persists across commit (put 5 -> read 5)" as *u8, (wc_sr_cursor(qAh3) == 5) as i64, pass, total)
339
340 // ---- T11 (R10 SHAPE-BASED QUERY ADMISSION -- replaces R8's host allowlist) -------------------
341 // R8 admitted query strings ONLY on curated hosts, so an uncurated content url was refused before
342 // a byte was fetched -- a DIRECTORY, not an index. R10 judges the query's SHAPE instead, so the
343 // SAME two fixtures now split on WHAT THE URL IS rather than WHO SERVES IT:
344 // clean content query, uncurated host -> ADMITTED (this is the whole point of R10)
345 // tracking param, uncurated host -> REFUSED (a duplicate of the clean url)
346 // Deny list set directly here, exactly as the canon table is a few lines below: the gate must be
347 // self-contained, never dependent on whatever the live urlpolicy- plane happens to hold today.
348 let qB: *u8 = "https://graphis.ne.jp/monthly/model.php?ID=260731_sp" as *u8
349 let qBjunk: *u8 = "https://tracker.example/page.php?utm_source=x" as *u8
350 var t11a: i64 = 0
351 if wc_urlok(qB, wc_len(qB)) == 1 { if wc_urlok(qBjunk, wc_len(qBjunk)) == 0 { t11a = 1 } }
352 gw_check("T11 R10: a CLEAN query url is ADMITTED with an EMPTY canon table; a TRACKING one is REFUSED" as *u8, t11a, pass, total)
353 // THE TOOTH THAT WOULD HAVE CAUGHT R8: the operator's real-world miss. An uncurated host serving
354 // paginated content must be reachable -- under R8 this was refused purely for lacking curation.
355 let qBv2: *u8 = "https://www.v2ph.com/actor/JULIA?page=3&hl=en" as *u8
356 gw_check("T11a-ii an UNCURATED host's paginated content url is ADMITTED (the R8 miss, by name)" as *u8, (wc_urlok(qBv2, wc_len(qBv2)) == 1) as i64, pass, total)
357 // and the trap half: a facet explosion beyond the param budget is still refused, on ANY host
358 let qBfacet: *u8 = "https://shop.example/list?a=1&b=2&c=3&d=4&e=5&f=6" as *u8
359 gw_check("T11a-iii a 6-param facet explosion is REFUSED (budget, not curation, is the limit)" as *u8, (wc_urlok(qBfacet, wc_len(qBfacet)) == 0) as i64, pass, total)
360 // now publish a canon table holding ONLY graphis.ne.jp
361 let qBcanh: *u8 = sys_mmap(64 * WC_MAXCANH)
362 var qBi: i64 = 0
363 let qBhost: *u8 = "graphis.ne.jp" as *u8
364 while qBhost[qBi] != (0 as u8) { qBcanh[qBi] = qBhost[qBi]; qBi = qBi + 1 }
365 qBcanh[qBi] = 0 as u8
366 wc_canh_g = qBcanh
367 wc_ncan_g = 1
368 wc_qscratch_g = sys_mmap(64)
369 // BISECT: which layer is failing -- the policy flag, or the canon match?
370 let t11p: i64 = olh_urlok_q(qB, wc_len(qB), 1)
371 gw_check("T11b-i policy layer: olh_urlok_q(allowq=1) admits the query-string url" as *u8, (t11p == 1) as i64, pass, total)
372 let t11m: i64 = wc_host_is_canon(qBcanh, 1, qB, wc_qscratch_g)
373 gw_check("T11b-ii matcher layer: wc_host_is_canon sees graphis.ne.jp in the table" as *u8, (t11m == 1) as i64, pass, total)
374 // R10: curation is now a BOOST, not a gate. The curated host keeps admitting its content, and the
375 // tracking url stays refused ON THE CURATED PATH TOO -- trust buys a bigger param budget, it does
376 // NOT buy immunity from junk. A curated host that could launder tracking urls into the index would
377 // make curation a liability rather than a signal.
378 var t11b: i64 = 0
379 if wc_urlok(qB, wc_len(qB)) == 1 { if wc_urlok(qBjunk, wc_len(qBjunk)) == 0 { t11b = 1 } }
380 let qBcurjunk: *u8 = "https://graphis.ne.jp/p.php?utm_source=x" as *u8
381 gw_check("T11b curated host ADMITS its content; a TRACKING url is refused on the CURATED path too" as *u8, (t11b == 1) as i64 * (wc_urlok(qBcurjunk, wc_len(qBcurjunk)) == 0) as i64, pass, total)
382 // the other kill-classes must NOT be weakened for the curated host either
383 let qBtag: *u8 = "https://graphis.ne.jp/tag/foo?ID=1" as *u8
384 let qBimg: *u8 = "https://graphis.ne.jp/i/photo.jpg" as *u8
385 var t11c: i64 = 0
386 if wc_urlok(qBtag, wc_len(qBtag)) == 0 { if wc_urlok(qBimg, wc_len(qBimg)) == 0 { t11c = 1 } }
387 gw_check("T11c curation relaxes ONLY '?' -- tag-farm and binary-extension kills still hold" as *u8, t11c, pass, total)
388 // T12 (R9): a CURATED host is never retired -- we cannot distinguish "host is down" from "we
389 // cannot speak its TLS", so retiring it would launder our own capability gap into permanent
390 // coverage loss. Uncurated rot must STILL retire (that is the control).
391 var t12: i64 = 0
392 let qCu: *u8 = "https://graphis.ne.jp/some/page" as *u8
393 let qCj: *u8 = "https://rotten.example/dead" as *u8
394 var qCcur: i64 = 0
395 var qCrot: i64 = 0
396 if wc_host_is_canon(qBcanh, 1, qCu, wc_qscratch_g) == 1 { qCcur = 1 }
397 if wc_host_is_canon(qBcanh, 1, qCj, wc_qscratch_g) == 0 { qCrot = 1 }
398 if qCcur == 1 { if qCrot == 1 { t12 = 1 } }
399 gw_check("T12 retire-exemption predicate: curated host protected, uncurated rot still retirable" as *u8, t12, pass, total)
400 wc_ncan_g = 0
401
402 gw_puts("=== crawl-step gate: " as *u8); gw_num(pass[0]); gw_puts("/" as *u8); gw_num(total[0]); gw_puts(" " as *u8)
403 if pass[0] == total[0] { gw_puts("GREEN\n" as *u8) } else { gw_puts("RED\n" as *u8) }
404 let ctr: *i64 = gv_ctr()
405 ctr[0] = pass[0]
406 ctr[1] = total[0]
407 return gv_verdict("WEB-CRAWL-STEP-GATE" as *u8, ctr, "frontier store mechanics: url filter, P-to-D state flip, cross-segment dedupe, outlink capture" as *u8)
408}