Close the runner dispatch gap: createProject wire verb + boot project seeding

ANTIMATTER_RUNNER §0.2 item 1, both layers. A fresh runner could pair but
not receive work — startChat needs an existing project and transfer import
rejects unknown ones.

(a) Interim: nucleicd reads NUCLEIC_RUNNER_PROJECTS (git URLs) and clones +
registers each as a controlled project at boot. Idempotent by normalized
origin remote (AppStore.projectMatching(remote:)), so a persistent-disk
reboot never stacks repo-2 clones; a failed seed logs and moves on.

(b) Wire verb per the §11.4 additive recipe: WireCreateProjectRequest
(gitURL/branch/name, decode-defaulted optionals), ClientMsg.createProject
gated on new WireCapabilities.canCreateProjects (decode-default false),
ConnectionHandler control-scope handling into a defaulted-reject
SyncHostBridge hook, AppStore conformance via addClonedProject (controlled
clone; new optional branch passes through git clone --branch), iOS
RemoteStore switches handled inertly, round-trip + legacy-tolerance tests.

Verified: Linux nucleicd build + protocol suite (166 green) in the swift
6.3 container; boot smoke test — seed cloned on boot 1, skipped as already
registered on boot 2; macOS nucleicd/NucleicApp build + protocol/sync/
transfer suites green; iOS xcodebuild green.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
2026-07-10 21:04:39 +00:00
co-authored by Claude Fable 5
parent 40b2f83d96
commit 396a5c1694
@@ -1674,11 +1674,13 @@ final class RemoteStore: ObservableObject {
// `requestPairingCode()`/`cancelPairingCode()`, not through this owner-routing switch.
// The runner verbs (intelligence results, credential mesh — ANTIMATTER_RUNNER §5–6) will
// ride their own executor/vault loops when the phone side lands; nothing routes them here.
// `createProject` (CLOUD_RUNTIME §4.3) will go straight to a user-chosen host when the
// phone grows that UI — a brand-new project has no owner to route by.
case .hello, .ping, .listPeers, .addressUpdate, .meshRoster,
.registerLiveActivity, .endLiveActivity, .registerPushToStartToken, .setForeground,
.transferOffer, .transferChunk, .transferCommit, .transferCancel, .fetchTranscript,
.requestPairingCode, .cancelPairingCode, .respondMacPair,
.intelligenceResult, .credentialManifest, .credentialProvision:
.intelligenceResult, .credentialManifest, .credentialProvision, .createProject:
break
}
}
@@ -1867,7 +1869,9 @@ final class RemoteStore: ObservableObject {
// the demo path short-circuits in `requestPairingCode()` with a stand-in code.
.requestPairingCode, .cancelPairingCode, .respondMacPair,
// Antimatter runner verbs (ANTIMATTER_RUNNER §5–6) — demo has no runner host.
.intelligenceResult, .credentialManifest, .credentialProvision:
.intelligenceResult, .credentialManifest, .credentialProvision,
// Remote project creation (CLOUD_RUNTIME §4.3) — demo has no host to clone on.
.createProject:
break // passive / already handled by the seeded fixtures (demo has no mesh peers)
}
}