The subprocessor disclosure's evidence stays where the dependencies are — re-home subprocessor-list-guard per the decision
Type: code · Executor: coding_agent · Repo: motir-core · ONE PR.
tests/legal/subprocessor-list-guard.test.ts is the only thing holding a published legal representation against reality, and its two halves live in different repositories the moment subprocessors.md moves. This card keeps the half that can only live here, and emits what the other half needs.
Why it cannot simply move
The guard reads TWO things:
| what it reads | where that lives after the move |
|---|---|
content/legal/subprocessors.md and content/legal/model-providers.md — the rows we DISCLOSE | motir-marketing |
package.json dependencies and outbound hosts in lib/ and app/ — the EVIDENCE that a vendor receives data (tests/helpers/subprocessorRegistry.ts's VENDOR_SIGNATURES, NOT_A_VENDOR_HOST, LEAVING_BEFORE_LAUNCH, INVISIBLE_TO_THIS_GUARD) | motir-core, and nowhere else |
Move the whole guard and it measures motir-marketing's dependency tree, which processes nothing — a guard that passes by construction. Delete it and the page goes back to being maintained by whoever happens to look, which its own header records failing four times on 2026-08-26/27, three of them fixed by re-measuring, "which works exactly once".
The change
- Keep the measurement here.
VENDOR_SIGNATURESand its three companion lists stay inmotir-core, run againstmotir-core's own tree. The registry's semantics are unchanged — a signature still means "this vendor will receive data at launch", not "this import exists right now" — andLEAVING_BEFORE_LAUNCHstill keeps a departing vendor's omission deliberate and attributable. - Emit the EGRESS MANIFEST. A committed artifact naming the vendor set the tree's evidence implies, plus whatever the ADR's chosen transport needs on it (a version, a source stamp). The guard becomes: the manifest matches what this tree's signatures measure — a
motir-core-only assertion with no page in it. - Ship it over the transport the decision card picks, and build exactly that. The decision's binding requirement is the PROPERTY — the seam FAILS when the two sides diverge — not a particular pipe; implement the pipe it names and do not substitute a copy with no drift check.
- Re-point the guard's registration.
tests/helpers/structuralGuardLane.tsliststests/legal/subprocessor-list-guard.test.tsin the structural-guard lane, with a comment naming bothcontent/legal/files. Both the entry and its comment are updated to the new subject. - Do NOT assert the page here. The page-side half — every vendor row on
subprocessors.mdhas a matching manifest entry — belongs tomotir-marketingand is the motir-marketing gate's. One repo, one suite, one PR.
⚠️ The absence question, asked of the guard itself
Per plan-rules/type-migration.md: "if this file were absent, what would start happening?" For this guard the answer is a published legal document silently drifts from what the software actually does — so it is a SUPPRESSOR-shaped element, not a client. It leaves LAST or not at all, and it may not be deleted as part of the page's move. Say so in the PR body.
Boundary
- It changes no page and no rendered surface.
- It does not touch
content/legal/. The files are still in the tree when this merges; the deletion card removes them, and it isblocked_bythis one precisely so the guard is re-homed before its subject leaves. - It does not add a vendor, remove one, or edit either document. If the measurement disagrees with the page, that is a finding to surface, not to fix by editing the page — the guard never edits the page, and that rule is unchanged.
- It does not decide the transport. The decision card does.
Acceptance criteria
tests/helpers/subprocessorRegistry.tsstays inmotir-corewith its four lists and their documented semantics intact; its header states that the disclosed page now lives inmotir-marketingand that this file is the evidence side.- A committed egress manifest exists in
motir-core, and a test asserts it matches whatVENDOR_SIGNATURESmeasures against this tree — a vendor with a live signature and no manifest entry FAILS, and a manifest entry with no signature FAILS. - Both directions are asserted with a real fixture, not by inspection: the PR shows the test going red when a signature is added and the manifest is not updated, and red in the opposite direction.
- The manifest is delivered to
motir-marketingby the transport the decision names, and the PR body names it and links the ADR section. tests/helpers/structuralGuardLane.ts's entry and its comment name the guard's new subject; no lane entry points at a file or a document that no longer exists.NOT_A_VENDOR_HOST's distinction survives — a host string in the tree is not proof anything is sent to it — and a test still covers a member of it.pnpm lint · format:check · typecheck · testpass; ≥90% coverage on any file this PR modifies rather than deletes.- The PR body answers the absence question for this guard in one line, and states that it is not deleted by the page's move.
Context refs
motir-core/tests/legal/subprocessor-list-guard.test.ts— the guard, and its header's account of the four staleness incidentsmotir-core/tests/helpers/subprocessorRegistry.ts—VENDOR_SIGNATURES,NOT_A_VENDOR_HOST,LEAVING_BEFORE_LAUNCH,INVISIBLE_TO_THIS_GUARDmotir-core/tests/helpers/structuralGuardLane.ts— the lane registration and its commentmotir-core/content/legal/subprocessors.md·model-providers.md— the documents leavingmotir-core/package.json— one half of the evidence- the decision card — Q3, the transport and the fail-on-divergence requirement;
blocked_by - the motir-marketing gate — the page-side half of the assertion
Advisory dispositions
- The remaining
referenceadvisories name cards this one's Boundary excludes. A boundary exists to say a deliverable is somebody else's, and naming the owner is what makes the exclusion checkable — so the reference is deliberate and no edge is owed.