The 772d15641 deploy (the #3128 conversation-scoped content-node re-keying) made graph_materialization's skip-when-unchanged stop firing: every run became a full-tenant scan, timed out after 1,508β2,516s, and 63 of 63 runs failed β every downstream phase blocked for Regina from 2026-08-16, ingestion paused, while the dedup pass had already merged nodes that should never have merged. This track is the gated repair: pause, capture undo, gate every merge surface on verified identity, re-materialize, re-merge, verify, unpause.
βοΈ The cost class grew its own track on 2026-08-26: the fold-cost attribution exploration (docs/exploration/20260826-regina-fold-cost-attribution.md β document extraction is not in the fold; two graph phases are 83.4% of it) and the doc-needs sizing program (ceiling fix merged as PR #3594; the top-K store and HNSW build filed) now live on the FOLD-EFFICIENCY page β including the silent doc-extract enqueue gap found on the way (issue #3600, closed 2026-08-31 β detail in History). This cohort keeps the re-keying follow-up and the incident's own measured costs.
Repair first, verification second, decisions third. The repair itself is done β so what orders the remainder is what still degrades answers silently. The stale embeddings outrank everything because every similarity surface reads them (the viewer-truth detector's “embedding-bounded floor” caveat and the dedup judge itself both inherit the staleness). The OCR-backlog question outranks the decisions because ~23,000 documents of spend may be running without its sign-off. The fold-efficiency program keeps its #3495 priority because it is what stops the incident class from recurring on the next deploy. The judge-quality cluster sorts last because it is real but was ALREADY the pre-incident state β the repair did not create it, it exposed it.
Deliberately ignored: priority labels alone; filing order; the sunk cost of the repair itself.
#3490. A closed issue named as current work is the drift this section exists to surface β re-derive the prose, or move the reference into History.
Evidence below is generated from the read-only audit of 2026-08-29 and reconciled against the open issues at build time. Every claim cites the issue it came from. Nothing here is a decision β rulings stay in the prose above, which is written by hand.
All three constants are present verbatim on origin/main backend/app/core/constants.py: line 192 `NAME_MAX_ARABIC_WORDS = 4`, line 201 `NAME_GROUP_TAG_BOILERPLATE = frozenset({"whatsapp","chat","group","the","and","for","with","of","in","to"})`, line 207 `NAME_COMMON_FIRST_NAMES` (Latin-script transliterations only). All three are consumed on the live ingestion path: `NAME_MAX_ARABIC_WORDS` at backend/app/orchestration/phases/name_resolution_phase.py:154 inside `_is_arabic_phrase`, wired into the phase's predicate list at line 203; the other two at backend/app/services/name_resolution/resolver.py:161 and :163 inside `NameResolverService.extract_group_tags`, itself called from name_resolution_phase.py:698 and resolver.py:746. Consumer census: `git grep -n "NAME_GROUP_TAG_BOILERPLATE|NAME_COMMON_FIRST_NAMES|NAME_MAX_ARABIC_WORDS|_is_arabic_phrase" origin/main -- backend` β the hits above plus backend/tests/unit/services/test_name_sanitization.py, which PINS the current behavior (line 105 asserts 'Ali Investment Group' keeps 'ali' via the first-name list). No replacement statistical/graph/LLM mechanism exists anywhere in that path.
Only the DOCUMENT leg was scoped to delta: commit 900ef0436 on origin/main ([fold-efficiency] fix(backend,db): scope fold document reads to per-conversation delta with an independent sync watermark (#3495) (#3521)). Its own commit message states 'PersonMaterializer is intentionally left untouched ... Flagged as owner-gated follow-up, not solved here.' No commit on origin/main since 2026-08-18 addresses the typed-item leg (git log origin/main --since=2026-08-18 -i --grep 3495 returns exactly two commits: 900ef0436 and dc71c85b1, the latter being CI wiring for #3502). Prod corroboration (public.ingestion_runs, last 5 days, grouped by tenant/day): the cost is still fleet-wide and heavy β 2026-08-28 tenant e9d96b09 (Regina) 637 runs / 51 failed / avg 1,724s / max 33,117s; tenant be0f3db0 178 runs / 18 failed / avg 559s / max 25,944s; 2026-08-27 avg 2,620s Regina and 2,280s on tenant 23d86e0d. Caveat: ingestion_runs durations are whole-run, not the graph_materialization substep alone.
No retention exists and the table is growing fast. (1) SELECT table_name FROM public.retention_policy_registry β 12 rows, graph_merge_snapshots is NOT among them (ama_turns, chat_threads, checkpoints, connector_staged_events, doc_need_matches, help_conversations, help_escalations, help_feedback, ingestion_runs, llm_usage_events, posthoc_resolution_proposals, work_queue). (2) The prod cron census (33 rows of cron.job, all enumerated) contains 12 prune-* jobs, none for graph_merge_snapshots. (3) Growth, measured: SELECT count(*), count(DISTINCT tenant_id), min/max(captured_at), pg_total_relation_size β 5,231 rows / 5 tenants / 2026-08-21 07:09Z β 2026-08-29 11:18Z / 126 MB. That is 228 rows and 6.0 MiB at the issue's 08-22 analysis comment β 5,231 rows and 126 MB seven days later: ~23x rows, ~21x bytes, on metered gp3 disk that does not shrink on its own.
origin/main:backend/app/slices/graph_building/repositories/node_index_repository.py:106-145 β `_PERSON_ATTRIBUTE_DIGEST_SQL` still runs five independent full-tenant aggregates (person_valuable_message_counts, person_impersonation_verdicts, person_automated_verdicts, whatsapp_jid_mappings, person_identities), each `string_agg`+`md5` with no watermark/delta predicate. Reachable on every fold: `graph_materializer.py:409` and `materialize_scan_planner_service.py:141` both call `person_attribute_input_digest()` unconditionally. `git log origin/main -- node_index_repository.py` shows the newest touch is #3355 (2026-07, WIRE-TRUTH), nothing after the 2026-08-22 filing.
Query on prod `public.ingestion_runs` (phase_times JSON), runs since 2026-08-22: cross-conv vs fold seconds β Regina e9d96b09: 5403 vs 726 (08-29 08:06Z), 4622 vs 1445 (08-29 04:09Z), 5694 vs 372 (08-28 23:11Z), 5315 vs 2038 (08-28 17:39Z); contrast tenant 23d86e0d: 128 vs 41, 109 vs 40, 97 vs 30. The premise holds and has WORSENED: on Regina cross-conv is 3-15x the fold and is measured in thousands of seconds. Code side: `git log origin/main --date=short -- backend/app/slices/graph_building/phases/graph_cross_conv_matching_phase.py` shows only #3594 (doc_need ceiling) and #3526 since filing β no diagnosis or perf change to propose/judge/write_edges.
Item 1 (bidi false positive) NOT fixed: origin/main:backend/scripts/regina_dedup_classify_bot_senders.py:84 still `_INVISIBLE_CATEGORIES = frozenset({"Cf"})`, which includes RLM/LRM/ALM, and line 137 fires `matched_rule="invisible_unicode"` on that alone with no corroboration requirement. Newest commit on that file is c7f9e4b18 (2026-08-21, PR #3513) β predates the 2026-08-22 sign-off that filed this. Item 2 (skip-list) absent: `git grep -rn "bot_" origin/main -- backend/app` (population = every file under backend/app on origin/main) returns only Slack connector bot_token/bot_id hits, no sender skip-list; `git grep -rn suppress -- graph_building/aggregators/person*.py` returns nothing. Item 3 (monitor) absent: no bot-mint check in `backend/app/orchestration/invariants/checks/` (10 files enumerated, none bot-related) and none of the 33 pg_cron jobs or 6 GH Actions crons drives one.
The production judge system prompt is unchanged since the issue was filed: `backend/app/slices/graph_building/services/dedup_judge.py` `_SYSTEM` (module read on origin/main) contains no domain-hint sentence β no "shared brand/base-name" clause, no size/packaging-qualifier clause; the same/sub_type/different definitions are the pre-existing generic ones. `git log origin/main --oneline --since=2026-08-23 -- backend/app/slices/graph_building/services/dedup_judge.py` returns no commit touching that file (the four commits in that window touch concurrency knobs, identity-signal chunking, doc-need ceiling and doc-project matching). The measured +0.08 precision / +0.16 recall arm has not been adopted, and none of the four stated next steps (larger stratified sample, held-out eval, panel, Finding-1 inconsistency probe) has landed.
The gate is unchanged and the behavior the issue describes is the coded design, not a regression: `backend/app/slices/graph_building/merge_partition_gate.py` module docstring Β§4.1 step 2 β "If any member carries identifiers, members with NO resolvable identifier at all do NOT ride in for free β each becomes its own singleton holdout" β implemented at lines 182-198 (`_disqualified_holdouts`, `singleton_holdout_ids`). Identifier sets are per-type (docstring line 7: person emailβͺJID; company employee email domains; location address tokens) with no company-specific alternative signal β no corroboration floor or structural check appears in the module (grep over the file for company/node_type/holdout/singleton returns only the docstring mentions and the generic holdout machinery). `git log origin/main --since=2026-08-23 -- backend/app/slices/graph_building/merge_partition_gate.py` shows no commit. The issue itself asks for the owner's judgment on whether company gets a less email-dependent verification signal; that ruling has not been made.
origin/main:backend/app/slices/graph_building/constants.py:364-374 β V2_NODE_DEDUP_THRESHOLD still holds exactly 8 keys (company, person, topic, skill, location, product, interest, trait); none of the 11 content types. origin/main:backend/app/slices/graph_building/phases/graph_dedup_phase.py:562 still reads `dedup_types = [t for t in present_types if t in V2_NODE_DEDUP_THRESHOLD]`, so those types are silently filtered out of every dedup pass. Prod re-measurement (supabase execute_sql, schema-qualified public.nodes_v2, 2026-08-29): the 11 types now total 46,276 nodes; grouping by canonical_name with the trailing `::<uuid>` conversation discriminator stripped gives 5,137 nodes sitting in a group with a sibling (11%). The leak half: bare (non-suffixed) nodes = 3,185, of which only 17 carry >1 provenance_conversations; only 2 nodes fleet-wide carry the `properties.leak_fix_batch` stamp (both with source_message_id NULL), so the null-the-anchor remediation described in the comments was never run at scale β the population it targeted has instead shrunk on its own (2,373 bare multi-conversation nodes at filing vs 17 now).
3 assessed issues closed since the audit and are not shown β a closed issue's evidence is history, and printing it as current is how a page starts lying.