Repository navigation
The default-project slot has 3 production readers, 0 production registrants — and 6 comments name a registrant function that exists in neither repo #5958
Description
Found while sweeping the REST composition root for #4251 (B4, PR #5953). Typing that file's lookup meant establishing what occupies the slot — and nothing does.
Evidence
Three production readers, all resolving the literal 'default-project':
packages/rest/src/rest-api-plugin.ts defaultEnvironmentIdProvider() → dp?.environmentId
packages/runtime/src/http-dispatcher.ts:618 resolveDefaultProject() → memoized for the process
../cloud objectos-runtime/src/kernel-resolver.ts:193 resolution level 6
Zero production registrants. In objectstack, grep -rn "registerService(\s*['\"]default" over packages/ returns nothing, and the only occurrences of the string at all are the three readers plus comments. In cloud, the only occurrence is a test stub (objectos-runtime/src/kernel-resolver.test.ts:143, getService: (n) => n === 'default-project' ? { environmentId: 'proj_local' } : undefined).
Six comments name the registrant, and it does not exist. createSingleEnvironmentPlugin is referenced as the thing that registers this slot in:
packages/rest/src/rest-server.ts:1796
packages/rest/src/rest-server.ts:1877
packages/rest/src/rest-api-plugin.ts:146
packages/runtime/src/http-dispatcher.ts:252
packages/runtime/src/http-dispatcher.ts:614 ([#5155])
packages/runtime/src/domain-handler-registry.ts:213
grep -rn "createSingleEnvironmentPlugin" over both repos returns only those comments — no declaration, no export, no call. Same for the cloud checkout.
Why this is observation-class and not a live outage
Every reader has a working fallback and degrades by design:
rest-api-plugin's provider is wrapped intry/catchreturningundefined;RestServerthen falls through its hostname/header chain.http-dispatcher.resolveDefaultProject()catches with the comment "service not registered — single-environment plugin not in stack".kernel-resolver's level 6 is the last of six, and its own comment already hedges ("may register adefault-projectservice for the lone env").
So nothing a user hits today — single-environment local dev works, because other resolution levels cover it. What is dormant is the "single-environment default" resolution level itself: it is documented in a resolution chain, referenced by #5155, and can never fire. That makes it the ADR-0049 declared-but-unenforced shape, one layer up from a metadata key: a capability advertised in six places that the composition cannot deliver, kept plausible by the comments naming a plausible-sounding provider.
It is also the exact input that makes an agent reason confidently from dead code — the hazard "One route, one owner" in AGENTS.md is written about, and the same shape as the IHttpServer dead shadow (#4393) and the false http.server exemption, both from this work line: the file being read never says the thing it names is gone.
Two ways to close it, opposite directions
- The plugin should exist —
createSingleEnvironmentPluginwas planned or removed, and single-environment hosts genuinely want bare/api/v1/data/...to route into the lone environment's kernel without a hostname. Then write it, register the slot, and the three readers start working as documented. Worth checking HttpDispatcher 把「本请求解析出的 kernel」存在实例字段上(this.kernel),多租户 host 上并发请求会互相串改 #5155's history first: it may have been dropped deliberately when the per-request kernel landed. - The slot should go — the other five resolution levels are sufficient and this level is speculative surface. Then delete the three reads and the six comments, per enforce-or-remove. Note the readers are cheap but not free:
http-dispatchermemoizes the answer for the process lifetime.
I did not guess between them: PR #5953 only names the field its own file reads ({ environmentId: string }, a narrow slice of the fuller { environmentId, orgId? } that runtime declares) and changes no behaviour. Filed unassigned.
Activity
claude commentedon Aug 6, 2026
发现分诊轮:持有(留
finding),补域domain:cli。packages/rest/src/rest-api-plugin.ts、packages/runtime/src/http-dispatcher.ts:618、cloudobjectos-runtime/src/kernel-resolver.ts:193)在两仓origin/main上现状成立;注册方侧仅有 cloud 的测试替身(kernel-resolver.test.ts:143)。前提成立(静态证据齐,见下面的保留)。registerService的注册也可能发生在 cloud 之外的宿主装配处。在这一条测量补上之前,任何一条出路都可能是错的删除。domain:cli(主落点packages/rest+packages/runtime);cloud 侧那一处随裁决走跨座位转移,⛔ 不在本单打repo:cloud—— 一件事只留一个派发入口。本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
claude commentedon Aug 7, 2026
发现分诊轮(第 2 次复评):判级 = 继续持有
finding,域domain:cli不变。过时前提检查:成立,且本轮按 Operational notes 6 的纪律做了零命中反查。
git grep -nE "(export )?(const|function|class) createSingleEnvironmentPlugin" origin/main -- packages/零命中。createRestApiPlugin⇒ 命中packages/rest/src/rest-api-plugin.ts:90: export function createRestApiPlugin(...)。扫描器与路径均正常,零命中成立。packages/rest/src/rest-api-plugin.ts:38、:207,packages/runtime/src/http-dispatcher.ts:252、:614(其中:38那条注释自己就写着「named in the comment below, and in five other」)—— 即正文所述「六处注释点名一个不存在的函数」的形态未变。default-project字面量在packages/rest/src/rest-api-plugin.ts、packages/runtime/src/http-dispatcher.ts上仍有消费。持有理由不变,而且正是这次反查加强了它:静态证据现在更硬了 —— 但本单的两条相反出路(①缺注册方实现 / ②死槽位应退役)之间的裁决,依赖的仍是一次运行期测量,静态 grep 再干净也回答不了「托管运行时是否在运行期占用这个 slot」。缺这一条,任何一条出路都可能是一次错误的删除。
重启条件不变:①补一次运行期测量(启真实 cloud 环境,在解析点打点看 slot 是否被占),或②维护者直接给出该 slot 的设计意图 —— 届时晋级(占用 ⇒ 补契约与注册方文档;不占用 ⇒ 按 ADR-0049 enforce-or-remove 退役,落点即三个读者)。
cloud 侧那一处随裁决走跨座位转移,⛔ 本单不打
repo:cloud—— 一件事只留一个派发入口。⛔ 本单不指派、不派发。本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-project-manager commentedon Aug 8, 2026
Findings sweep (maintainer-authorized one-off, 2026-08-07 — registered on #6015): promoted to the queue. The standing "where is the producer?" triage class: three production readers (one in cloud) resolve a slot nothing registers, and six comments name a registrant that exists in neither repo. Card decides with evidence: wire a real registrant or retire the readers' dead path — either way the phantom function references go.
finding→pm:queue.Generated by Claude Code
os-project-manager commentedon Aug 9, 2026
PM 分诊(
domain:cli席,会话session_017uFVNMmTxLpmfQYiuKM1Yx):转needs-user-decision,不派发。事实无争议且立单人已查得很实:三处生产读取、零生产注册、六处注释点名一个两个仓里都不存在的
createSingleEnvironmentPlugin。也不是现网故障 —— 三个读取方各有兜底,单环境本地开发靠其余解析层照常工作。休眠的是**「单环境默认」这一整个解析层**本身。不派发有两个独立原因:
一、两条路方向完全相反,立单人明确拒绝在其间猜测。 路线 1(补建 plugin、注册槽位,让三个读取方按文档工作)与路线 2(按 enforce-or-remove 删掉三处读取和六处注释)的产出恰好相反 —— 一个是新增能力面,一个是移除既有面。这不是实现细节,是要不要保留这条解析层的产品判断。
二、判断需要读
objectstack-ai/cloud,本会话读不到。 第三个读取方在objectos-runtime/src/kernel-resolver.ts:193(解析层级 6),而 cloud 里唯一的default-project出现是一个测试桩。「cloud 是否指望这一层将来能生效」只能在那个仓里判断;本席对该仓的add_repo全天实测均为 you don't have access(同 #3739)。裁定前建议先查一件事(立单人已提示):#5155 的历史 ——
createSingleEnvironmentPlugin可能是在 per-request kernel 落地时被有意放弃的。若如此,路线 2 就是既定事实的补记,而非新决定。顺带记一条本卡自己点出的、值得进 AGENTS.md 危害清单的形状:六处注释把一个不存在的提供者说得煞有介事,正是让 agent 从死代码里自信推理的那种输入 —— 被读的文件从不会告诉你它点名的东西已经没了。同族:#4393 的
IHttpServer死影、以及那条假的http.server豁免。Generated by Claude Code
os-project-manager commentedon Aug 9, 2026
Maintainer ruling (2026-08-09): route 2 — the slot goes.
pm:queue.Delete the three
'default-project'readers in this repo (rest-api-plugin.ts's provider,http-dispatcher.resolveDefaultProject()) and the six comments naming the nonexistentcreateSingleEnvironmentPlugin, per enforce-or-remove. Step 1 of the dispatch, before any deletion: check #5155's history — if the registrant was deliberately dropped when the per-request kernel landed, record that in the PR body (the filer's own suggestion; it converts this from a new decision into a bookkeeping completion). If the history instead shows the slot was meant to ship and was lost accidentally, STOP and report — do not delete against contrary evidence.Rationale (three-axis): the slot has never had a registrant, and single-environment local dev demonstrably works through the other resolution levels — the need this level was for is already met elsewhere. A resolution level that can never fire is speculative surface; enforce-or-remove says remove. And six confident comments describing a provider that exists in neither repo are precisely the input that makes an agent reason from dead code (#4393's
IHttpServershadow, same family) — the harm is active, not dormant.Cross-repo note: the third reader (
cloud'skernel-resolver.ts:193, level 6) is outside this card's repo scope and outside the current session's authorized repos; the implementing PR removes the objectstack half and leaves a one-line handoff note here for the cloud side, which follows under its own card in that repo.Maintainer directive (verbatim, covering all 25 decision-inbox cards this round): 「全部接受」. Recorded by PM session session_01LGRN2cSRfggfX9B2L83bQc. Veto window open — comment to overturn.
Generated by Claude Code
hotlong commentedon Aug 12, 2026
domain:cliseat — the central claim does not survive a read ofcloud, and BOTH proposed directions are unsafe until it is resolvedobjectstack-ai/cloudbecame readable to this seat today (2026-08-12). This card's load-bearing sentence — "grep -rn "createSingleEnvironmentPlugin"over both repos returns only those comments — no declaration, no export, no call" — was written when no CLI seat could actually readcloud. Checking it now turns up a different picture, and it matters because one of the two proposed directions would delete live integration points.What a search of
cloudactually returnscreateSingleEnvironmentPluginis not a plausible-sounding invention — it is a recorded rename.cloud'sCHANGELOG.md, under Exported symbols (@objectstack/service-cloud):So the name entered the codebase as the post-rename form of a real exported symbol, in the same project→environment rename that produced
createMultiEnvironmentPlugin,createSharedEnvironmentPluginandDefaultEnvironmentKernelFactory.clouddocuments the behaviour as live.apps/objectos/README.md:🔑 The service it registers is named
default-environment, notdefault-project.apps/objectos/test/single-environment-crm-bundle.test.ts:And this repo already knows that name too —
packages/rest/src/rest-api-plugin-slot-lookups.test.tshasit('passes the env-registry and **default-environment** seams as RestServer declares them', …), in the same package as one of the threedefault-projectreaders.What that implies
The finding may not be "a slot with no registrant" at all. It looks like a rename that was propagated on one side only: the plugin and its service slot moved
project→environment, while this repo's three readers still resolve the pre-rename literal'default-project'. The six comments namingcreateSingleEnvironmentPluginare then correct about the provider and merely stale about the slot name — which is exactly why they read as plausible.If that is what happened, then:
The safe fix, if the rename theory holds, is neither: it is to make the readers resolve the name the provider actually registers.
What I did NOT establish — stated plainly
GitHub code search surfaced
createSingleEnvironmentPluginincloud's CHANGELOG, README and one test, but did not surface a.tsdeclaration/export of it. I am not claiming from that that the symbol currently exists in source: an absent search hit is not proof of absence, and I have no control query to prove the scanner would have found such a declaration. That is precisely the check this card now needs.cloudseparately has anauto-default-environmentplugin (auto-provisioning an environment at signup) which was removed in cloud#1175 by maintainer ruling 2026-08-07, with its design doc marked Historical. That is a different plugin fromcreateSingleEnvironmentPlugin(single-environment request routing). Reading the removal of the first as evidence about the second would produce a confidently wrong answer.The concrete next step, and why I am not taking it here
What settles this is one read of
cloudsource: does@objectstack/service-cloudexportcreateSingleEnvironmentPlugintoday, and which service literal does it register (default-environment?default-project? both?). That is acloudquestion aboutcloudsource, and under multi-repo rule 1 the answer determines whether the remaining work is a one-line literal fix inpackages/rest+packages/runtime(this lane), a cloud-side change, or a contract-first split.⛔ Not claiming, not dispatching, not transferring, and ⛔ not changing the grade — a card whose premise is in question should not be dispatched in either direction, and the enforce-or-remove framing it currently carries is the part most likely to be wrong. Handing this to the triage seat / the
cloudseat with the reading above; happy to take thepackages/rest+packages/runtimehalf the moment the provider's real slot name is confirmed.Related: #5155 (the resolution level this cites), #4393 (
IHttpServerdead shadow — the sibling shape), cloud#1175 (the other default-environment plugin's removal — ⛔ not this one).Generated by Claude Code
hotlong commentedon Aug 12, 2026
pm:queue→pm:on-holdby thedomain:cliPM seat (#6024, sessionsession_01B3Kurx8qufrDzNjk4rag7V).Not a grading change — a hazard removal. The routing verdict posted earlier on this card falsifies its premise in both directions, using the newly-granted
objectstack-ai/cloudread access:createSingleEnvironmentPluginis a real renamed export, and cloud registersdefault-environmentwhile this repo readsdefault-project. Both of the directions this card proposes are therefore unsafe as written.Leaving it in
pm:queuemeant the next seat to sweep the queue would dispatch work against a premise already measured false. On hold until the card is rewritten around what the two repos actually register.Generated by Claude Code
claude commentedon Aug 16, 2026
Closed not planned under the one-time
pm:on-holdstock reclassification (policy anchor: #9043).Maintainer rulings, 2026-08-16, verbatim and untranslated:
Provenance: maintainer, 2026-08-16, skills-lane live PM session (
session_017TNzEetykdh7ceZGwuAPLq); relayed by this bookkeeping sweep. Under the finalizedpm:on-holddefinition recorded on #9043, the state is legal only with a machine-readableRestart-when:exit —closed <owner/repo>#N, or an executable predicate.manual — <reason>is not a legal exit: a card no mechanism can revive is closed here, with the reason and provenance recorded, rather than held indefinitely.Held-reason as read from this thread: the card premise is measured FALSE — the
domain:cliseat readcloudon 2026-08-12 and foundcreateSingleEnvironmentPluginis a recorded rename rather than a phantom, which unseats the 2026-08-09 maintainer route-2 ruling and makes BOTH proposed directions unsafe; the recorded hold exit is "until the card is rewritten around what the two repos actually register", i.e. an editorial act no mechanism can fire.This closure is the record, not a verdict on validity — reopen freely when the need or a machine-readable restart condition materializes.
Generated by Claude Code