# fix\(orchestration\): persist linked pull requests · PR \#8689 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/8689)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: merged

Source repository: pingdotgg/t3code

Source branch: agents/mcp-thread-metadata/persistence

Target branch: t3code/codex-turn-mapping

Head commit: 1d9e75452a1c2fe86590c864defd62fbe7f7d328

[Discussion and reviews](https://git.cafe/pingdotgg/t3code/pull/8689)

[Commits](https://git.cafe/pingdotgg/t3code/pull/8689/commits?format=markdown)

[Files changed](https://git.cafe/pingdotgg/t3code/pull/8689/diffs?format=markdown)

## Description

---

## Problem

The V2 metadata command accepts linked pull requests, but the decider drops that field, so links never reach the persisted event or projection.

## Change

Apply `linkedPullRequest` during metadata updates while preserving absent-field compatibility. Focused runtime coverage proves link, shell projection, projection rebuild, and unlink behavior.

## Validation

- `vp test run apps/server/src/orchestration-v2/runtimeLayer.test.ts` (14 tests)
- Targeted lint for the two changed files
- `vp run --filter t3 typecheck`

## Dependency

Based directly on `agents/mcp-controls/base-490318a` at the agreed `490318a` prerequisite snapshot. Native stack #8709: #8689 → #8690. The MCP metadata tool remains a separate dependent layer; no feature code changed when the rewritten live trunk was replaced as the review base.

Implemented by GPT-5.6-Sol via Codex in T3 Code.


<!-- Macroscope's pull request summary starts here -->
<!-- Macroscope will only edit the content between these invisible markers, and the markers themselves will not be visible in the GitHub rendered markdown. -->
<!-- If you delete either of the start / end markers from your PR's description, Macroscope will append its summary at the bottom of the description. -->
> [!NOTE]
> ### Fix `Orchestrator.layer makeOrchestrator` to persist `linkedPullRequest`
> The `makeOrchestrator` function now includes `linkedPullRequest` in the built object when the command supplies it, including `null` to represent unlinking. When `command.linkedPullRequest` is `undefined`, the field is omitted from the payload. Adds a lifecycle test covering link, projection rebuild, and unlink flows in [runtimeLayer.test.ts](https://github.com/pingdotgg/t3code/pull/8689/files#diff-c3988987d24088cbfd5b91a9324f2dc85bb6d6c7caf03bab1e007498467ac96e).
>
> <!-- Macroscope's review summary starts here -->
>
> <sup><a href="https://app.macroscope.com">Macroscope</a> summarized 1d9e754.</sup>
> <!-- Macroscope's review summary ends here -->
>
<!-- Macroscope's pull request summary ends here -->

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Small, targeted decider fix with regression tests; no auth or broad behavioral changes beyond persisting an already-defined metadata field.
> 
> **Overview**
> Fixes a gap where **`thread.metadata.update`** accepted `linkedPullRequest` but the V2 orchestrator decider never applied it, so links never made it into thread state, events, or projections.
> 
> The **`thread.metadata.update`** handler in `Orchestrator.ts` now merges `linkedPullRequest` when the command sets it (including **`null`** to unlink), and omits the field when it is **`undefined`**, matching other optional metadata fields.
> 
> A runtime-layer test covers linking a PR, reading it from the thread projection and shell, surviving a projection rebuild, and unlinking via `linkedPullRequest: null`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 1d9e75452a1c2fe86590c864defd62fbe7f7d328. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->


