ProjectionService.For now composes multiple tracks instead of a single
"parent" stream. The viewed project always emits Track="parent"; visible
CCR children emit Track="counterclaim:<child_id>"; a project that is
itself a CCR (counterclaim_of != nil) pulls its target's events as
Track="parent_context:<parent_id>" so the lawyer working the CCR sees
the main proceeding without leaving the page (§4.5).
Each track runs the actuals + projection pipeline independently with
its own lookahead cap and dependency annotations against its own
proceeding's rule tree. SubProjectID + SubProjectTitle are populated on
non-parent rows so the frontend can render the sub-project title in the
column sub-header.
ProjectionMeta gains AvailableTracks; the handler surfaces it as the
new X-Projection-Tracks response header (CSV) so the wire shape stays
[]TimelineEvent (frozen since Slice 1).
POST /api/projects/{id}/counterclaim wraps ProjectService.CreateCounterclaim
— accepts proceeding_type_id / flip_our_side / title / case_number,
returns the new project's id + canonical /projects/<id> URL.
Tests: pure-function coverage for derivedCounterclaimOurSide (default
flip + R.49.2.b override + court/both pass-through). Live-DB integration
test covers the four invariants — CreateCounterclaim atomicity (parent
audit + child audit + our_side flip + sibling-under-patent placement),
parent's projection surfaces the counterclaim track, child's projection
surfaces parent_context, two-level CCR chains are rejected by both the
service guard and the schema-level trigger.