plan-graph-substrate
Populate the plan-side twin of the bug body_preview/MENTIONS_CODE bridge (w-e3e502d2) so the ALREADY-WIRED related-nodes --kind plan|all query (query_graph.py:2643) has real signal instead of querying a NULL-corpus-wide plan_text fulltext index. Verified this session: the query path exists and degrades gracefully (try/except around the plan_text lookup), but returns zero plan candidates in practice because no import step feeds Plan.name/Plan.full_name. This section is a PREREQUISITE for plan-dedup-module’s plan-vs-plan leg — the dedup module can be authored against the wired query regardless, but its plan-vs-plan verdicts stay dark until this section lands real text into the index.
Work Items
- Investigate intel_repo/neo4j/import_plan_bug_graph.py’s Plan-node creation path: identify which import step creates :Plan nodes and why Plan.name/Plan.full_name are NULL on the live corpus today (which Plan properties ARE populated, e.g. plan_id/status).
- Wire plan title/mission text into Plan.name and Plan.full_name at import time in the SAME importer plan loop (import_plan_bug_graph.py:374-427) that builds plan_props, using a 3-tier fallback so every one of the 121 :Plan nodes lands non-NULL in both properties: (1) for v7-shape plans, derive from content/overview/mission.md’s title heading (name) and Mission opening sentence (full_name - the load-bearing Lucene field, since many mission headings are bare slugs) via the sidecar already present in plan.get(“sidecars”) in the same scope; (2) for v2-markdown plans that already carry a populated title prop (27 of 121, sourced via export_json.py’s _export_markdown_plan), fall back to that title for both name and full_name when no mission sidecar is present; (3) fall back to plan_id as the floor when neither source is available, so no plan is left NULL. No export schema change, no SCHEMA_VERSION/EXPECTED_SCHEMA_VERSION bump - derivation stays import-side-only.
- Wire Plan.body_preview from the same mission sidecar content in the same importer change (import_plan_bug_graph.py plan loop) that sets name/full_name, activating the existing Phase-3 MENTIONS_CODE bridge for :Plan nodes (Plan is already listed in _LABEL_TO_SOURCE_TYPE, import_plan_bug_graph.py:732, and _build_mentions_for_node already activates on a body_preview prop) so plan-kind related-nodes hits gain the shared_symbols additive tier the bug side already has, instead of permanently ranking on fulltext alone.
- Confirm the Neo4j plan_text fulltext index definition covers the newly-populated Plan.name/full_name properties (the SAME properties cmd_related_nodes already queries at query_graph.py:2691-2709).
- Run the plan-node property census (MATCH (p:Plan) WITH count(p) AS total, count(p.name) AS with_name, count(p.full_name) AS with_full_name RETURN total, with_name, with_full_name) after the importer change lands and the plan/bug graph re-syncs, confirming ALL 121 :Plan nodes carry non-NULL name AND full_name - the completeness gate the section’s own design requires (populate ALL plans regardless of status), not just a passing spot-check on one plan.
- Extend intel_repo/tests/test_related_nodes.py with a probe asserting related-nodes —kind plan —text "
" returns that plan with a non-zero score against the synced graph, AND records/reports the live plan-kind self-match score range so it is observable against GRAPH_FIRED_THRESHOLD=35.0 (scripts/add_bug_runtime/relatedness.py:108) - the threshold was calibrated on bug-kind (title+subsystem) score distribution, not plan-kind (short heading + one sentence) Lucene length-norm, so a bare non-zero-score assertion can pass while every real plan hit still scores below the consumer’s firing threshold. Confirm the existing ClientError-only graceful-degrade path (query_graph.py:2736-2751) is unchanged and still distinguishes a genuine no-match (ok/count:0 on a real query with no candidates) from the prior NULL-corpus dark-signal false-empty this section fixes - do not broaden the except to make the test pass.
Fresh intel (regenerated)
{ “schema_version”: 1, “target”: { “kind”: “plan-section”, “ref”: “graph-native-bug-relatedness/s-f5e63376” }, “generated_at”: “2026-07-14T05:21:16.676921+00:00”, “graph_state”: { “head_sha”: “d281786e3”, “last_code_import_at”: “2026-07-14T04:49:34.628000000+00:00”, “embedding_stale”: false, “insights_stale”: false, “cpg_stale”: false }, “surfaces”: {}, “summary”: “intel-package[plan-section:graph-native-bug-relatedness/s-f5e63376] agent-authored dossier”, “degraded”: false, “dossier”: { “objective_symbols”: [], “tiers”: {}, “agent_authored”: true, “constrained_symbols”: [ “SCHEMA_VERSION”, “EXPECTED_SCHEMA_VERSION” ], “difficulty”: { “class”: “routine”, “research_online”: false, “research_mode”: “auto”, “signals”: [], “plan_dir”: “/home/eric/projects/ori_lang/plans/graph-native-bug-relatedness” } } }
(full dossier: plan-graph-substrate—s-f5e63376.intel.json)