code wiki / _hdl_build / nx_sitededup.nx
nx_sitededup.nx
buildroot/runtime/_hdl_build/nx_sitededup.nx
about
nx_sitededup.nx -- COLLAPSE DUPLICATE DOCUMENTS IN A SITE SEARCH SHARD.
THE DEFECT, measured 2026-08-25 on a live scope=site SERP for "nishi search": of 30 results,
SIX were the SAME document under two different cids -- e.g. "VORTEX RISC-V GPGPU -- COMPLETE
PUBLICATION BIBLIOGRAPHY" as 7477184972746352550 AND 2839761502039440748, with IDENTICAL scores.
ROOT CAUSE: a cid is a content hash of the RAW STORED BYTES, and TWO DIFFERENT HASH FUNCTIONS write
the same doc:<cid> keyspace -- dp_cid (FNV-1a, nx_docportal_lib) and ci_hash (base-131 polynomial,
nx_corpus_ingest). Two captures of one page that differ only in a Date: header or a revision line
are therefore two different documents, and cross-path dedup is impossible by construction.
Ingest has since been fixed FORWARD (dp_ingest_policy strips capture headers and runs bd_fit_text
before hashing). That does nothing for rows already stored. This organ is the backward half.
---- WHY THE FIRST VERSION OF THIS ORGAN COULD NOT HAVE WORKED, AND HOW WE KNOW ----------------
The first cut fingerprinted the RAW stored bytes. Its own gate then measured the real capture pair
-- one document, stored twice, the second copy carrying an HTTP header and a bumped revision line
-- at SIX bits of Hamming distance against the published k=3 bar. It would have collapsed NOTHING.
The published recipe has a step the fingerprint papers do not cover: NORMALISE THE TEXT BEFORE
SHINGLING, and specifically normalise DIGITS TO A CONSTANT, which is what kills counter, date,
revision and price near-duplicates. That is precisely our duplicate class. nx_textnorm_lib is that
step, it is a SHARED lib so the gate proves the same normalisation the actuator ships, and this
organ now reports the raw-vs-normalised comparison ON THE WHOLE POPULATION so the claim
"normalisation is what made this work" is a measurement rather than an assertion.
WHY SUPPRESSION AND NOT DELETION: rule 13, additive-only. Nothing is removed. A duplicate is marked
by appending a pol:<cid> row with the public-search consent bit cleared -- the SAME row the search
path ALREADY reads per candidate (dss_search, DSS_POL_SEARCH), so this needs no change to any serve
organ and takes effect on the next query. It is reversible by appending a row with the bit set.
Every other owner flag on that row is preserved (sdd_clear_search_bit).
WHY DURABLE AND NOT AT RANK TIME: the alternative is fingerprinting every candidate on every query.
Fingerprinting is a full pass over the document TEXT, and the serve path deliberately does not read
doc bodies during candidate filtering (the site: filter's own comment: "Cheap: ss_hget per candidate,
no doc reads"). Paying a text scan per candidate per query to rediscover a fact that does not change
between queries is the wrong side of the trade. This pass pays it once.
COMPOSES, NEVER RE-IMPLEMENTS: nx_simhash for the fingerprint (Charikar 2002 / Manku 2007),
nx_textnorm_lib for the ONE copy of the normalisation decision, nx_sitededup_lib for the ONE copy
of the collapse decision, dss_mkpolkey for the policy row, and ccz_cat_num as the integer emitter.
THE FINGERPRINT PLANE IS DELIBERATELY A NEW KEYSPACE. Persisted fingerprints go to fpn:<cid>
dependencies 4 imports · 0 importers
imports: nx_corpus_ingest.nxnx_sitededup_lib.nxnx_textnorm_lib.nxnx_itoa_lib.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 55 | const SD_KEYCAP: i64 = 64 |
| 56 | const SD_VALCAP: i64 = 32 |
| 57 | const SD_WORD: i64 = 8 |
| 58 | const SD_MINUS: i64 = 45 |
| 59 | const SD_ZERO: i64 = 48 |
| 60 | const SD_NINE: i64 = 57 |
| 61 | const SD_DEC: i64 = 10 |
| 62 | const SD_URL_MINKEY: i64 = 5 |
| 63 | const SD_URL_MAXKEY: i64 = 60 |
| 64 | const SD_C_U: i64 = 117 |
| 65 | const SD_C_R: i64 = 114 |
| 66 | const SD_C_L: i64 = 108 |
| 67 | const SD_C_COLON: i64 = 58 |
| 68 | const SD_C_C: i64 = 99 |
| 69 | const SD_C_A: i64 = 97 |
| 70 | const SD_C_P: i64 = 112 |
| 71 | const SD_C_D: i64 = 100 |
| 72 | const SD_C_O: i64 = 111 |
| 73 | const SD_PREFIXCAP: i64 = 512 |
| 74 | const SD_BOX: i64 = 8 |
functions
| 76 | func sd_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } |
| 77 | func sd_num(v: i64) -> i64 |
| 84 | func sd_atoi(s: *u8) -> i64 called by 1: main |
| 93 | func sd_parse_i64(p: *u8, n: i64) -> i64 |
| 106 | func sd_usage() -> i64 |
| 122 | func sd_fp_of(h: *i64, cid: i64, fkey: *u8, dkey: *u8, ap: *i64, al: *i64, box: *i64, nb: *i64, out: *i64) -> i64 called by 1: main calls 6: tn_mkfpnkeyss_hgetsd_parse_i64dss_mkkeynx_simhash_fingerprinttn_fingerprint_box |
| 147 | func main(argc: i64, argv: *i64) -> i64 |