relatedness-graph-rewrite
Goal
Rewrite bug-vs-plan relatedness to consume the graph query and delete the regex/Jaccard heuristics from the relatedness decision path.
Implementation Sketch
Deliver the relatedness-graph-rewrite slice of the graph-native relatedness + dedup capability per the plan mission; ground every claim against the verified intel-graph facts in the overview.
Work Items
- Rewrite compute_relatedness to rank active plans via the related-nodes graph query, reusing scripts/intel_package_runtime/surfaces.py:run_intel_query (add
related-nodesto READ_SUBCOMMANDS) rather than a new subprocess wrapper. Query plan-kind hits and JOIN them to the active-plan roster onp.plan_id == plan_dir.name: the graph ranks plans (fulltext + subsystem bonus),discover_active_plansresolves section granularity,status, andis_feature_plan/is_bug_planexclusions the graph does not carry — graph-ranks-plans, local-roster-resolves-section, never graph-replaces-discovery. Define and calibrate the fired/not-fired ranking threshold via an A/B parity probe (today’s heuristic verdict vs. the graph candidate) across a historical-bug sample — the section-4 closed misroute family (BUG-07-170/-254/-225/-211/-234/-193/-197/-190) makes the fixture set. Decide and implement the signals_fired vocabulary mapping for graph-based scoring consistent with routing.md §2’s 3-signal table (reuse an existing label or add one); implement the mapping here — the doc-side update is skill-surface (s-6beb9df5)‘s scope, coordinate rather than duplicate. Treat shared_symbols as a compiler-domain-only bonus tier, never a gate — only 119/2,748 bug MENTIONS_CODE edges resolve to a :Symbol (all compiler-side), so a wrapper-Python-only bug or plan gets shared_symbols: [] forever and must still be rankable on fulltext + subsystem alone. Preserve the two-axis RelatednessResult contract (signals_fired / recommended_action) byte-compatible for bug_tracker_io.append_open_bug and main’s verdict serialization. - Fail open identically (no signal fired -> default-route append, parity with today’s empty-plan branch, never raise) across every non-genuine-no-relation state, without internally conflating them: (1) graph-down — related-nodes/run_intel_query returns status: unavailable; (2) dark-signal — a queried kind’s backing fulltext index has no populated text (the state plan-graph-substrate, s-f5e63376, closes for plan-kind before this section now executes, per the forward-coherence reorder ahead of this section); (3) genuine no-relation — the query ran, ranked candidates, and every candidate scored below the fired threshold. Collapsing dark-signal into no-relation silently was the exact defect class the section-4 bug family already forced a cure for on the query side (BUG-07-193/197/190-adjacent fixes) — do not reintroduce it on the consumer side. Reuse scripts/intel_package_runtime/surfaces.py:run_intel_query (add related-nodes to READ_SUBCOMMANDS) rather than a second subprocess wrapper; never raise.
- Remove compute_relatedness’s decision-path calls into _extract_symbols / _extract_paths / _tokenize_words / the Jaccard scorer (_signal_success_criteria) / _signal_file_set / _signal_symbol / the CamelCase stoplists (_TRACKER_VOCAB_STOPLIST / _COMMON_CAP_ENGLISH / _is_single_hump / _is_sentence_initial) — the graph query replaces this scoring. KEEP the underlying function definitions in signals.py intact: the public SSOT wrapper extract_symbol_candidates (a LIVE consumer via scripts/fix_bug_runtime/plan_coverage_signals.py:26) calls _extract_symbols directly and must keep working — do not delete _extract_symbols or _SYMBOL_REGEX. Only the public wrapper functions tokenize_words / extract_paths are candidates for removal, and only once section 04 (enhance-wiring, w-d2e63b6e) confirms zero remaining external consumers when it lands — do not double-delete; their disposition stays owned by section 04 as already recorded there.
- Update test_relatedness*.py + test_inline_signals.py to drive the graph-query path (inject a fake related-nodes envelope) instead of asserting regex internals, split into two distinct actions per suite: (a) DELETE tests asserting deleted-heuristic internals directly (e.g. test_relatedness_english_stoplist.py’s direct _COMMON_CAP_ENGLISH references); (b) PORT every behavioral pin currently expressed through a regex-shaped fixture (CamelCase tokens, path prefixes) to an equivalent graph-envelope fixture, preserving the disposition-layer pins intact end-to-end — active-status filtering, bug-plan exclusion, feature-plan subsumption (test_relatedness_dedup_matrix.py’s BUG-07-193/197/190 regression pins) — each with a positive+negative pin pair (related-entry-redirects / unrelated-entry-appends / graph-down-appends). A behavioral pin deleted rather than ported is INVERTED-TDD:positive-test-modification. Extend (do not duplicate) test_related_nodes_query.py’s production-entry-point pattern so the real CLI (python -m scripts.add_bug_runtime append / relatedness-check) is exercised end-to-end on at least one real-shaped entry (L12 per layer-coverage.md §1).
Fresh intel (regenerated)
The graph substrate this section binds to is MORE finished than the section body assumes on the bug side, and STILL DARK on the plan side — and the section’s own work-item text points at the dark half. Four decisive findings:
- [JOIN] w-75511079 as written lands on a dark substrate. The work item says “rank active plans via the related-nodes graph query (plan-kind hits intersected with the active-plan set)” — but
related-nodes --kind planreturns ZERO candidates live (verified this session:Plan.name/full_nameare NULL on all 121:Plannodes; theplan_textfulltext index exists but indexes nothing), and the section that populates it (plan-graph-substrate,s-f5e63376) sits FIVE positions later in the strict-linear walk (02 vs 07). Executing this section as written produces a relatedness path that NEVER fires a signal until s-f5e63376 lands — a silent behavioral regression window vs today’s heuristics (which, however false-positive-prone, do catch true positives). Decision forced BEFORE work: either (a) restructure — pulls-f5e63376ahead of this section (forward-coherence /routing.md §4-style reorder via/review-section), or (b) design the interim explicitly — rank via--kind bugco-mention + plan-STATUS-bearing:Plannodes (p.plan_id+p.statusARE populated) or accept + document the dark window with/review-bugsas the stated net. Option (a) is the shape that avoids shipping a knowingly-inert deliverable. - The bug-side substrate is fully live — richer than the section text or the older sibling dossiers claim. Verified this session: the w-fef32852 gate inversion is FIXED at HEAD (fulltext runs unconditionally on prose-only text — live probe returned BUG-07-170/-254/-193 at Lucene 5.1-5.7 for plain prose); the w-e3e502d2 enrichment bridge is LIVE (2,748 Bug-side
MENTIONS_CODEedges, 984/984 bugs carrybody_preview); the--subsystemequality bonus tier works (+1.0 verified live). The frozen envelope{id, kind, title, status, score, shared_symbols}is real and this section is its FIRST consumer — shape changes stop being cheap the moment this section lands. - [JOIN] The shared-symbol tier can never corroborate this domain’s own bugs. The Symbol graph indexes
compiler_repoonly (rust 26,033 + ori 10,570 symbols; ZERO wrapper-Python symbols — verified). Only 119 of 2,748 bug MENTIONS_CODE edges resolve through to a:Symbol, all compiler-side (plus junk likeor/failed). A tooling bug namingcompute_relatednessgetsshared_symbols: []forever (probe confirmed). The rewrite’s scoring model must treatshared_symbolsas a compiler-domain-only bonus, never a gate — fulltext score + subsystem bonus IS the substrate for tooling bugs. - The deletion item (w-4aa37e23) has a live external blocker the section text half-anticipates.
extract_symbol_candidatesis imported byscripts/fix_bug_runtime/plan_coverage_signals.py:26viascripts.add_bug_runtime.apias the declared SSOT CamelCase extractor (“a local bare-CamelCase branch is BANNED” per its own comment), andpackage_symbol_evidenceis pinned byscripts/intel_package_runtime/tests/test_consumers.py+test_consumer_required_reading.py. The delete scope is exactly what the work item says — “from the relatedness DECISION PATH” — not file-wide:signals.py’s extractor + stoplists survive as the fix_bug_runtime SSOT unless that consumer migrates (out of this section’s scope). Onlytokenize_words/extract_pathspublic wrappers are verified dead (zero external callers; sibling sectionenhance-wiringw-d2e63b6e owns their final disposition — coordinate, don’t double-delete).
Also forced before code: the signals_fired VOCABULARY question. _recommended_case (case a/b), _is_feature_plan_subsumption (dedup-into-section), .claude/skills/add-bug/SKILL.md/workflow.md, and routing.md §2’s 3-signal table all consume the literal labels file-set/symbol/success-criteria. Preserving “the two-axis RelatednessResult contract” therefore means deciding what graph evidence maps to which label (or introducing a graph label and updating every consumer + rule doc in the same change) — this is the largest hidden surface of the rewrite.
["extract_symbol_candidates", "package_symbol_evidence"]
(Must-preserve external-contract surfaces: extract_symbol_candidates has a live cross-runtime consumer in fix_bug_runtime/plan_coverage_signals.py; package_symbol_evidence is pinned by intel_package_runtime consumer tests and re-exported by api.py. Deleting either file-wide breaks consumers outside this plan. The section’s own deliverables — compute_relatedness, the decision-path internals, cmd_related_nodes — are work-subject, not constraints.)
DIG DEEPER
scripts/intel-query.sh related-nodes --text "any prose at all" --kind plan --limit 5 # 0 rows = plan-kind dark
scripts/intel-query.sh cypher "MATCH (p:Plan) WHERE p.name IS NOT NULL OR p.full_name IS NOT NULL RETURN count(p)" --human
python -m scripts.plan_corpus.read --summary plans/graph-native-bug-relatedness
grep -n "extract_symbol_candidates" scripts/fix_bug_runtime/plan_coverage_signals.py
(full dossier: relatedness-graph-rewrite—s-c7999cd5.intel.md)