Pillar A — typed cross_plan_relations[] + derived reverse
Goal
The v7 schema carries a typed cross_plan_relations[] authored in ONE direction; the reverse edge is DERIVED by export_json, never hand-authored, so it cannot drift.
Implementation Sketch
Augment cross_plan_needs[] into cross_plan_relations[] {plan, id, kind: dependency|non_contradiction|mission_enforces, rationale, surface}. Synthesize the reverse edge in export_json._emit_plan_edges (mirrors existing DEPENDS_ON/BLOCKED_BY derivation). Forward-only migration backfills []; consumer version pin bumps.
Spec References
scripts/plan_corpus/schemas/v7/plan-routing.schema.json (Section.cross_plan_needs lines 485-497); scripts/plan_corpus/export_json.py:_emit_plan_edges (335-400). routing.md §5 ordering primitives.
Work Items
- Add
cross_plan_relations[]to the v7 schema ($defs Section + WorkItem) with kind/rationale/surface fields; keep cross_plan_needs as the dependency subset alias - Derive the reverse edge in export_json._emit_plan_edges — reverse is computed, never stored (kills bidirectional drift)
- Forward-only migration backfilling
cross_plan_relations: []+ bump the consumer plan-corpus-version pin - Schema validator (reject malformed kind/surface) + pytest