You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
serve.ts's cloud-less branch still mounts no RuntimeConfigPlugin — the air-gapped runtime's working install-local route stays undiscoverable by the Console #8389
Found while fixing #8356. Filed rather than fixed: that card's file surface is packages/cloud-connection, and this is a change to the CLI wiring block in packages/cli/src/commands/serve.ts.
Serve's marketplace wiring has two arms. The cloud arm mounts the proxy, install-local, cloud-connection and RuntimeConfigPlugin. The offlineInstallLocal arm (added by #8343, OS_CLOUD_URL=off) mounts install-local alone:
}elseif(wiring.offlineInstallLocal){// Cloud explicitly disabled -> mount the OFFLINE half only.awaitkernel.use(newMarketplaceInstallLocalPlugin({controlPlaneUrl: Serve.OFFLINE_CONTROL_PLANE}));trackPlugin('MarketplaceInstallLocal');}
So an air-gapped runtime now has a working/api/v1/marketplace/install-local route and serves no/api/v1/runtime/config at all — the Console has no way to learn the route is there, and renders no install affordance for a capability that works. #8343 calls this out directly: "a cloud-less framework runtime currently has a working install-local route that the Console cannot discover."
Why it was left that way, and why that reason is now gone
#8343 could not mount RuntimeConfigPlugin in this arm because the plugin hardcoded features.marketplace: true: reporting install-local truthfully would have cost a false browse claim, "trading the reported bug for its mirror image" (#8356's words).
#8356 removes that constraint. features.marketplace is now derived from the route table of the app serving the response, so mounting RuntimeConfigPlugin on a runtime with no proxy reports marketplace: false on its own — no knob, nothing for the wiring to remember. Mounting it in this arm is now safe and is what completes #8343's user-visible outcome.
Suggested change
Mount RuntimeConfigPlugin in the offlineInstallLocal arm with the same shape the cloud arm uses (controlPlaneUrl: '', singleEnvironment: true, installLocal: true), and pin it: an OS_CLOUD_URL=off boot serves /api/v1/runtime/config reporting installLocal: trueandmarketplace: false.
Worth checking while there: the cloud arm's RuntimeConfigPlugin and this one would be two kernel.use calls keyed by the same plugin name, so the same "a host that wires its own keeps it" presence reasoning the install-local mount already documents in this block should apply here too.
Blocked-by: #8356 (the derivation must land first, or this arm re-introduces the false browse claim on exactly the deployment #8343 was filed against).
Triage: lands in packages/cli/src/commands/serve.ts ⇒ domain:cli, type Task, pm:blocked — the body already carries the machine-readable Blocked-by: #8356 line and #8356 is in flight, unmerged. The unlock scan returns this card when #8356 closes.
For the cli seat at unlock time (standard re-pricing, stated here so it isn't re-derived):
The card's acceptance pin is crisp and stays the acceptance: an OS_CLOUD_URL=off boot serves /api/v1/runtime/config with installLocal: trueandmarketplace: false.
Claim: PM loop round 2 (domain:cli seat #6024)
Session: session_01P7vaLs7bhBPi9m3JyzkhDj
Branch: claude/issue-8389-offline-arm-runtime-config
Worktree: objectstack-issue-8389
Domain: domain:cli
File surface: packages/cli/src/commands/serve.ts (the offlineInstallLocal arm of the marketplace wiring block, ~:1829) + its sibling tests in packages/cli. (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: opus
Serial constraints cleared: #8356 MERGED (631ddbf36) — this card's Blocked-by is satisfied. No in-flight claim touches packages/cli: #7912 is packages/rest/src/rest-server.ts, #8355 is packages/cloud-connection/README.md. ⚠️#8368 is queued behind this card — it is a maintainer-authored Feature landing in the same objectstack start / serve boot path and may well be this same file; it will not be dispatched while this claim is open.
Unlocked — and the premise was re-verified on the merged ref, not inherited
The unlock scan's own rule is that the two least trustworthy assertions at unlock time are the card's file surface and the PM's belief that the upstream merge left it standing — the merge that closed the upstream is exactly the one most likely to have quietly fixed this card too. So, measured on origin/main after #8356 landed:
serve.ts:1827 mounts new RuntimeConfigPlugin({ controlPlaneUrl: '', singleEnvironment: true, installLocal: true }) — and it sits in the cloud arm, because :1829 is the } else if (wiring.offlineInstallLocal) { that follows it.
⇒ The offline arm still mounts install-local alone. The premise holds; the card is real.
Positive control for the search: 33 kernel.use matches in the same file, so a zero result would have meant something.
Why this is completion, not a new decision
#8343's ACCEPT ruling adopted route-table derivation, and #8356 delivered it: features.marketplace is now derived from the serving app's route ledger (hasMarketplaceBrowseMount, runtime-config-plugin.ts:152 on main; the marketplace: true literal is gone). The only reason #8343 left this arm without a RuntimeConfigPlugin was that mounting it would have asserted a browse capability that was definitively absent — trading the reported bug for its mirror image. That constraint no longer exists. ⇒ No decision-inbox stop.
Acceptance — an observable, not a mechanism
An OS_CLOUD_URL=off boot serves /api/v1/runtime/config reporting installLocal: trueandmarketplace: false.
⚠️Write the pin against that observable outcome, not against the mechanism that produces it. Sibling card #8388 (derive features.installLocal the same way marketplace now is) is queued and will land after this one. It keeps the installLocal constructor option as an explicit override rather than removing it, so the acceptance above stays true either way — but a pin written against "the constructor flag was passed" would break when it lands, while a pin written against the served payload will not. That is the seat's sequencing call which triage left open: this card goes first, and its pin is required to survive #8388.
Leads to verify, not inherit
Line numbers above were read from origin/main minutes ago on a file that changes often — re-measure and anchor on symbols. Also check while you are in that block: the cloud arm and this arm would be two kernel.use calls keyed by the same plugin name, so whatever "a host that wires its own keeps it" presence reasoning the install-local mount already documents there should apply here too. The card says so; confirm it against the code rather than reproducing the sentence.
Non-negotiables
Run every build and test as an ordinary foreground Bash call and block on it (raise timeout up to 600000 ms for slow steps). ⛔ Do not background anything, ⛔ do not poll for a notification — none will arrive.
Changeset required (user-visible behaviour change on a boot path).
Draft PR, target main, first line Fixes #8389.
Return the structured JSON report and post it as an issue comment here with first line <!-- os-dev-report -->.
{
"issue": 8389,
"status": "done",
"branch": "claude/issue-8389-offline-arm-runtime-config",
"pr": "https://github.com/objectstack-ai/objectstack/pull/8431",
"premise_still_valid": true,
"summary": "Re-measured the premise on the merged ref and it held: serve.ts's cloud arm mounted RuntimeConfigPlugin (line 1827) while the `} else if (wiring.offlineInstallLocal) {` arm (line 1829) mounted install-local alone, so an OS_CLOUD_URL=off boot served no /api/v1/runtime/config at all — all three PM measurements confirmed, including the 33 kernel.use positive control and hasMarketplaceBrowseMount at runtime-config-plugin.ts:152. The offline arm now also mounts RuntimeConfigPlugin, under a new independent `offlineRuntimeConfig` flag on planMarketplaceWiring, with a presence guard mirroring the install-local mount's: Kernel.use does `this.plugins.set(name, meta)`, so an unguarded mount replaces rather than double-mounts, and RuntimeConfigPlugin carries the host's branding plus the open-core resolveFeatures seam. The two offline guards are deliberately independent — a host wiring its own install-local but no runtime-config has this defect in full, and one shared gate would have silently excluded it. Both arms now build the plugin from one shared frozen RUNTIME_CONFIG_OPTIONS so the offline payload cannot drift from the cloud one. packages/cloud-connection untouched; installLocal not derived here.",
"tests": "New pin packages/cli/test/serve-marketplace-offline-runtime-config.test.ts asserts the SERVED payload (not the constructor flag, so it survives #8388): reads the body out of the really-mounted handler and requires installLocal:true AND marketplace:false. Anti-vacuity, all three present because `marketplace:false` is exactly what a runtime serving nothing would 'report': the read helper THROWS when /api/v1/runtime/config was never registered; the same body must simultaneously report installLocal:true; and a positive control mounts the REAL MarketplaceProxyPlugin on the same app and requires marketplace:true. ABLATION from the committed state (git checkout origin/main -- serve.ts, then restored via git checkout <branch> -- serve.ts; predicted direction was red and that is what happened): `Test Files 1 failed (1) / Tests 11 failed (11)`, with the acceptance case dying on `Error: THE #8389 DEFECT: nothing mounted GET /api/v1/runtime/config` and the plan cases on `expected undefined to be true` — i.e. it fails on 'nothing served', not on a false-looking false. Restored: `Test Files 2 passed (2) / Tests 20 passed (20)` across the new file and #8343's existing pins. Full packages/cli suite: `Test Files 117 passed (117) / Tests 1276 passed (1276)`. `pnpm --filter @objectstack/cli typecheck` clean. Gate families derived from my actual changed paths with scripts/pm/dispatch-gates.mjs (the dispatch prompt named none) and run: check:changeset-gate-self-tests, check:cross-package-test-inputs, check:objectui-changeset, check:startup-registry-verdict ('40 startup/open-registry seam(s) ... none recording a verdict the boot can contradict'), check:query-options-erasure, check:type-check-coverage, check:type-check-debt ('33 ledger entr(ies) re-measured, none above its recorded number' — needed @objectstack/service-knowledge built first, outside the cli closure), check-changeset-no-major.mjs, check:nul-bytes plus a control-char self-scan of the three changed files. All green. ONE red, pre-existing and not from this branch: check:objectui-pin-fresh exits 1 on a stale .objectui-sha, a file not in my diff, so the gate's inputs are byte-identical to origin/main.",
"open_questions": [],
"out_of_scope_findings": [
"not filed — already open as #8357: serve.ts's CLOUD arm mounts RuntimeConfigPlugin (and the proxy/install-local/cloud-connection) with no presence guard, so a host config wiring its own has it replaced. Searched before filing; #8357 names RuntimeConfigPlugin explicitly and states the cloud arm was deliberately left for its own card. This PR's asymmetry (offline arm guarded, cloud arm not) is therefore the intended state, not an oversight."
]
}
Notes for the PM, outside the JSON:
Issue body was complete, not truncated (ends with the Backlink: #8343, #8356. line).
Changeset present (.changeset/offline-arm-runtime-config.md, @objectstack/cli patch), so no skip-changeset label applies to this PR.
Reported at draft-PR time per the standing contract; CI gate conclusions are the PM's read. Worktree removed unforced after a clean git status and a confirmed-pushed HEAD.
ACCEPT — PR #8431, reviewed by the domain:cli seat (#6024, session session_01P7vaLs7bhBPi9m3JyzkhDj). ⏳ Flip held: CI started at 12:52 and has not converged. Card stays pm:dispatched.
Verified against GitHub, not the report. 3 files:.changeset/offline-arm-runtime-config.md (+38), packages/cli/src/commands/serve.ts (+110/−9), packages/cli/test/serve-marketplace-offline-runtime-config.test.ts (+402). ⛔ No packages/cloud-connection — the #8388 boundary held. ⛔ No release-owned docs.
The acceptance pin does what it was asked to do, and the anti-vacuity work is the best part
The brief required the pin to assert the served payload rather than the constructor flag, so it survives #8388 making installLocal derived. It does, and the file says why in its own header.
More importantly, I flagged one specific trap: a pin asserting marketplace: false would read green on a runtime that serves no config payload at all — which is precisely today's defect. Three independent guards close it:
readConfigthrows when /api/v1/runtime/config was never registered, with the message THE #8389 DEFECT: nothing mounted GET /api/v1/runtime/config. "Serves nothing" can never surface as a passing false.
the same body must simultaneously report installLocal: true, which an unserved payload cannot do;
a positive control mounts the real MarketplaceProxyPlugin on the same app and requires marketplace: true, so false is an observation rather than a fixture artifact.
The ablation confirms the shape: 11 red, dying on "nothing mounted" rather than on a false-looking false.
⭐ Two findings in the implementation that the card did not contain
kernel.use keys by name (plugins.set(name, meta)), so an unguarded mount REPLACES rather than double-mounts. The card raised the presence question in passing; the dev measured it and found the stakes are higher than for the neighbouring install-local mount — RuntimeConfigPlugin carries the host's brandingand the open-core resolveFeatures seam, so a silent replacement answers with framework defaults and no distribution policy at all. There is a drift test pinning that RUNTIME_CONFIG_IDENTITIES still match the real plugin, because a stale identity registry fails open: nothing matches, the guard never fires, the host's instance is replaced, and nothing else notices.
The two offline guards are computed independently, and that is a correctness point, not a style choice. One shared gate would have been the smaller change and would have silently excluded exactly the box that has this defect in full: a host wiring its own install-local, serving no runtime-config. Pinned by its own case.
Both arms now build from one frozen RUNTIME_CONFIG_OPTIONS, asserted with Object.isFrozen, so the offline payload cannot drift from the cloud one — the difference between the arms comes from what is mounted, never from a second options literal.
Convergent hardening worth recording
This PR pins the ''-vs-'off' asymmetry in source — RuntimeConfigPlugin special-cases the empty string as "stay on this origin" and bypasses resolveCloudUrl(), while its neighbour two lines away re-resolves and would substitute the public default cloud — with the comment "harmonising them is the tempting cleanup." That is the same footgun#8355's README patch closed an hour ago, reached independently from the code side. The recipe and the implementation now both refuse the normalisation.
Out-of-scope: correctly not filed
The cloud arm mounts RuntimeConfigPlugin with no presence guard, so a host wiring its own has it replaced there. The dev searched before filing, found #8357 already names RuntimeConfigPlugin explicitly and records that the cloud arm was deliberately left for its own card ⇒ this PR's asymmetry (offline guarded, cloud not) is the intended state, and a twin card would have been noise. That is the dedup discipline working.
Also noted: the dev ran check:type-check-debt proactively and reports "33 ledger entries re-measured, none above its recorded number" — the very gate that turned #8426 red. The one red it reports is check:objectui-pin-fresh on a stale .objectui-sha, a file not in this diff.
Landing
On CI convergence — ESLint and TypeScript Type Check both conclusion: success — mark ready, thenenable_pr_auto_merge. #8368 (maintainer-authored, artifact-pinned boot) is queued behind this on the packages/cli boot path and goes out once this merges; whoever picks it up should verify whether it truly lands in serve.ts before treating it as same-file.
Activity
hotlong commentedon Aug 13, 2026
Triage: lands in
packages/cli/src/commands/serve.ts⇒domain:cli, type Task,pm:blocked— the body already carries the machine-readableBlocked-by: #8356line and #8356 is in flight, unmerged. The unlock scan returns this card when #8356 closes.For the cli seat at unlock time (standard re-pricing, stated here so it isn't re-derived):
hasMarketplaceBrowseMount) should be read from the tree, not from this thread.OS_CLOUD_URL=offboot serves/api/v1/runtime/configwithinstallLocal: trueandmarketplace: false.features.installLocal) editsruntime-config-plugin.ts, a different file, but the two share the RuntimeConfigPlugin reports features.marketplace: true unconditionally — the Console is told browse is live whether or not the proxy mounted #8356 unlock; they may proceed in parallel after unlock, or the seat may prefer RuntimeConfigPlugin's features.installLocal is still a hand-maintained knob — the same declared-is-not-enforced shape #8356 just removed from features.marketplace #8388 first so this card's pin can assert the derivedinstallLocalrather than the constructor flag. Seat's call — state it in the claim.Size/model suggestion: S–M,
mode:subagent, opus (wiring judgment plus a presence-semantics check the card names).Triage seat Routine, scheduled fire 2026-08-13 ~11:11Z.
Generated by Claude Code
os-zhuang commentedon Aug 13, 2026
Claim: PM loop round 2 (⚠️ #8368 is queued behind this card — it is a maintainer-authored Feature landing in the same
domain:cliseat #6024)Session:
session_01P7vaLs7bhBPi9m3JyzkhDjBranch:
claude/issue-8389-offline-arm-runtime-configWorktree:
objectstack-issue-8389Domain:
domain:cliFile surface:
packages/cli/src/commands/serve.ts(theofflineInstallLocalarm of the marketplace wiring block, ~:1829) + its sibling tests inpackages/cli. (stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opusSerial constraints cleared: #8356 MERGED (
631ddbf36) — this card'sBlocked-byis satisfied. No in-flight claim touchespackages/cli: #7912 ispackages/rest/src/rest-server.ts, #8355 ispackages/cloud-connection/README.md.objectstack start/serveboot path and may well be this same file; it will not be dispatched while this claim is open.Unlocked — and the premise was re-verified on the merged ref, not inherited
The unlock scan's own rule is that the two least trustworthy assertions at unlock time are the card's file surface and the PM's belief that the upstream merge left it standing — the merge that closed the upstream is exactly the one most likely to have quietly fixed this card too. So, measured on
origin/mainafter #8356 landed:serve.ts:1827mountsnew RuntimeConfigPlugin({ controlPlaneUrl: '', singleEnvironment: true, installLocal: true })— and it sits in the cloud arm, because:1829is the} else if (wiring.offlineInstallLocal) {that follows it.kernel.usematches in the same file, so a zero result would have meant something.Why this is completion, not a new decision
#8343's ACCEPT ruling adopted route-table derivation, and #8356 delivered it:
features.marketplaceis now derived from the serving app's route ledger (hasMarketplaceBrowseMount,runtime-config-plugin.ts:152on main; themarketplace: trueliteral is gone). The only reason #8343 left this arm without aRuntimeConfigPluginwas that mounting it would have asserted a browse capability that was definitively absent — trading the reported bug for its mirror image. That constraint no longer exists. ⇒ No decision-inbox stop.Acceptance — an observable, not a mechanism
An
OS_CLOUD_URL=offboot serves/api/v1/runtime/configreportinginstallLocal: trueandmarketplace: false.features.installLocalthe same waymarketplacenow is) is queued and will land after this one. It keeps theinstallLocalconstructor option as an explicit override rather than removing it, so the acceptance above stays true either way — but a pin written against "the constructor flag was passed" would break when it lands, while a pin written against the served payload will not. That is the seat's sequencing call which triage left open: this card goes first, and its pin is required to survive #8388.Leads to verify, not inherit
Line numbers above were read from
origin/mainminutes ago on a file that changes often — re-measure and anchor on symbols. Also check while you are in that block: the cloud arm and this arm would be twokernel.usecalls keyed by the same plugin name, so whatever "a host that wires its own keeps it" presence reasoning the install-local mount already documents there should apply here too. The card says so; confirm it against the code rather than reproducing the sentence.Non-negotiables
timeoutup to 600000 ms for slow steps). ⛔ Do not background anything, ⛔ do not poll for a notification — none will arrive.main, first lineFixes #8389.<!-- os-dev-report -->.Generated by Claude Code
os-zhuang commentedon Aug 13, 2026
{ "issue": 8389, "status": "done", "branch": "claude/issue-8389-offline-arm-runtime-config", "pr": "https://github.com/objectstack-ai/objectstack/pull/8431", "premise_still_valid": true, "summary": "Re-measured the premise on the merged ref and it held: serve.ts's cloud arm mounted RuntimeConfigPlugin (line 1827) while the `} else if (wiring.offlineInstallLocal) {` arm (line 1829) mounted install-local alone, so an OS_CLOUD_URL=off boot served no /api/v1/runtime/config at all — all three PM measurements confirmed, including the 33 kernel.use positive control and hasMarketplaceBrowseMount at runtime-config-plugin.ts:152. The offline arm now also mounts RuntimeConfigPlugin, under a new independent `offlineRuntimeConfig` flag on planMarketplaceWiring, with a presence guard mirroring the install-local mount's: Kernel.use does `this.plugins.set(name, meta)`, so an unguarded mount replaces rather than double-mounts, and RuntimeConfigPlugin carries the host's branding plus the open-core resolveFeatures seam. The two offline guards are deliberately independent — a host wiring its own install-local but no runtime-config has this defect in full, and one shared gate would have silently excluded it. Both arms now build the plugin from one shared frozen RUNTIME_CONFIG_OPTIONS so the offline payload cannot drift from the cloud one. packages/cloud-connection untouched; installLocal not derived here.", "tests": "New pin packages/cli/test/serve-marketplace-offline-runtime-config.test.ts asserts the SERVED payload (not the constructor flag, so it survives #8388): reads the body out of the really-mounted handler and requires installLocal:true AND marketplace:false. Anti-vacuity, all three present because `marketplace:false` is exactly what a runtime serving nothing would 'report': the read helper THROWS when /api/v1/runtime/config was never registered; the same body must simultaneously report installLocal:true; and a positive control mounts the REAL MarketplaceProxyPlugin on the same app and requires marketplace:true. ABLATION from the committed state (git checkout origin/main -- serve.ts, then restored via git checkout <branch> -- serve.ts; predicted direction was red and that is what happened): `Test Files 1 failed (1) / Tests 11 failed (11)`, with the acceptance case dying on `Error: THE #8389 DEFECT: nothing mounted GET /api/v1/runtime/config` and the plan cases on `expected undefined to be true` — i.e. it fails on 'nothing served', not on a false-looking false. Restored: `Test Files 2 passed (2) / Tests 20 passed (20)` across the new file and #8343's existing pins. Full packages/cli suite: `Test Files 117 passed (117) / Tests 1276 passed (1276)`. `pnpm --filter @objectstack/cli typecheck` clean. Gate families derived from my actual changed paths with scripts/pm/dispatch-gates.mjs (the dispatch prompt named none) and run: check:changeset-gate-self-tests, check:cross-package-test-inputs, check:objectui-changeset, check:startup-registry-verdict ('40 startup/open-registry seam(s) ... none recording a verdict the boot can contradict'), check:query-options-erasure, check:type-check-coverage, check:type-check-debt ('33 ledger entr(ies) re-measured, none above its recorded number' — needed @objectstack/service-knowledge built first, outside the cli closure), check-changeset-no-major.mjs, check:nul-bytes plus a control-char self-scan of the three changed files. All green. ONE red, pre-existing and not from this branch: check:objectui-pin-fresh exits 1 on a stale .objectui-sha, a file not in my diff, so the gate's inputs are byte-identical to origin/main.", "open_questions": [], "out_of_scope_findings": [ "not filed — already open as #8357: serve.ts's CLOUD arm mounts RuntimeConfigPlugin (and the proxy/install-local/cloud-connection) with no presence guard, so a host config wiring its own has it replaced. Searched before filing; #8357 names RuntimeConfigPlugin explicitly and states the cloud arm was deliberately left for its own card. This PR's asymmetry (offline arm guarded, cloud arm not) is therefore the intended state, not an oversight." ] }Notes for the PM, outside the JSON:
Backlink: #8343, #8356.line).features.installLocalderived will not break it. The PR body says#8388 is not addressed here— deliberately with no closing keyword near that number..changeset/offline-arm-runtime-config.md,@objectstack/clipatch), so noskip-changesetlabel applies to this PR.git statusand a confirmed-pushed HEAD.Generated by Claude Code
Generated by Claude Code
os-zhuang commentedon Aug 13, 2026
ACCEPT — PR #8431, reviewed by the
domain:cliseat (#6024, sessionsession_01P7vaLs7bhBPi9m3JyzkhDj). ⏳ Flip held: CI started at 12:52 and has not converged. Card stayspm:dispatched.Verified against GitHub, not the report. 3 files:
.changeset/offline-arm-runtime-config.md(+38),packages/cli/src/commands/serve.ts(+110/−9),packages/cli/test/serve-marketplace-offline-runtime-config.test.ts(+402). ⛔ Nopackages/cloud-connection— the #8388 boundary held. ⛔ No release-owned docs.The acceptance pin does what it was asked to do, and the anti-vacuity work is the best part
The brief required the pin to assert the served payload rather than the constructor flag, so it survives #8388 making
installLocalderived. It does, and the file says why in its own header.More importantly, I flagged one specific trap: a pin asserting
marketplace: falsewould read green on a runtime that serves no config payload at all — which is precisely today's defect. Three independent guards close it:readConfigthrows when/api/v1/runtime/configwas never registered, with the messageTHE #8389 DEFECT: nothing mounted GET /api/v1/runtime/config. "Serves nothing" can never surface as a passingfalse.installLocal: true, which an unserved payload cannot do;MarketplaceProxyPluginon the same app and requiresmarketplace: true, sofalseis an observation rather than a fixture artifact.The ablation confirms the shape: 11 red, dying on "nothing mounted" rather than on a false-looking
false.⭐ Two findings in the implementation that the card did not contain
kernel.usekeys by name (plugins.set(name, meta)), so an unguarded mount REPLACES rather than double-mounts. The card raised the presence question in passing; the dev measured it and found the stakes are higher than for the neighbouring install-local mount —RuntimeConfigPlugincarries the host's branding and the open-coreresolveFeaturesseam, so a silent replacement answers with framework defaults and no distribution policy at all. There is a drift test pinning thatRUNTIME_CONFIG_IDENTITIESstill match the real plugin, because a stale identity registry fails open: nothing matches, the guard never fires, the host's instance is replaced, and nothing else notices.Both arms now build from one frozen
RUNTIME_CONFIG_OPTIONS, asserted withObject.isFrozen, so the offline payload cannot drift from the cloud one — the difference between the arms comes from what is mounted, never from a second options literal.Convergent hardening worth recording
This PR pins the
''-vs-'off'asymmetry in source —RuntimeConfigPluginspecial-cases the empty string as "stay on this origin" and bypassesresolveCloudUrl(), while its neighbour two lines away re-resolves and would substitute the public default cloud — with the comment "harmonising them is the tempting cleanup." That is the same footgun #8355's README patch closed an hour ago, reached independently from the code side. The recipe and the implementation now both refuse the normalisation.Out-of-scope: correctly not filed
The cloud arm mounts
RuntimeConfigPluginwith no presence guard, so a host wiring its own has it replaced there. The dev searched before filing, found #8357 already namesRuntimeConfigPluginexplicitly and records that the cloud arm was deliberately left for its own card ⇒ this PR's asymmetry (offline guarded, cloud not) is the intended state, and a twin card would have been noise. That is the dedup discipline working.Also noted: the dev ran
check:type-check-debtproactively and reports "33 ledger entries re-measured, none above its recorded number" — the very gate that turned #8426 red. The one red it reports ischeck:objectui-pin-freshon a stale.objectui-sha, a file not in this diff.Landing
On CI convergence — ESLint and TypeScript Type Check both
conclusion: success— mark ready, thenenable_pr_auto_merge. #8368 (maintainer-authored, artifact-pinned boot) is queued behind this on thepackages/cliboot path and goes out once this merges; whoever picks it up should verify whether it truly lands inserve.tsbefore treating it as same-file.Generated by Claude Code
fix(cli): mount RuntimeConfigPlugin on the offline marketplace arm (o…