Recording only — OBSERVATION class. ⛔ Not claimed, no assignee, no domain:*, no type. Filed by the domain:cli PM seat while reviewing PR #12149 (#11614). Dedupe run before filing: no open card covers this detector; the closest matches are the same class in other gates and are all closed (see Precedent).
The mechanism
packages/cli/src/commands/serve-cluster-host-resolution.test.ts sweeps serve.ts for dynamic loads and asserts each app-declarable one is host-anchored. To do that it must resolve a specifier held in a variable, via resolveIdentifier().
⭐ The failure direction is the dangerous one: a specifier it cannot resolve drops OUT of APP_DECLARABLE_LOADS rather than into it. An unrecognised spelling therefore does not fail the sweep — it silently removes that load from the population being judged, and every assertion downstream keeps passing over a smaller set.
That is not hypothetical. It fired during #11614: moving the @objectstack/organizations spelling onto a class static made the specifier unresolvable, and the organizations load stopped being swept. PR #12149 repairs it correctly — in the detector, by resolving one hop further (const X = Serve.MEMBER → that class's static readonly MEMBER), which strictly widens the judged population and adds no allowlist, ratchet or baseline.
What is still open after that repair
The repair teaches the resolver one further spelling. The class remains:
- the second hop is anchored on the literal prefix
Serve., so a static on any other class or namespace is unresolvable; - it requires the trailing
;; - it requires
static readonly on the same file.
Any other spelling — a let, a destructure, an object member, a re-exported constant, or passing the static straight into the call site with no intermediate const — resolves to undefined and takes its load quietly out of the sweep.
⚠️The only backstop is hand-maintained. The vacuity guard has two halves, and only one of them can see this:
expect(APP_DECLARABLE_LOADS.length).toBeGreaterThan(20);// ← cannot see one member vanish…for(constpkgof['@objectstack/service-cluster','@objectstack/service-cluster-','@objectstack/organizations','@objectstack/service-i18n']){expect(found,`the sweep no longer sees the ${pkg} load`).toContain(pkg);// ← the half that caught it}⭐ A vacuity guard that COUNTS cannot detect a specific member disappearing from its population. The > 20 floor absorbed the organizations loss without a word; the named list is what caught it, and it covers four packages chosen by hand. A fifth load added tomorrow, spelled a way the resolver does not know, is invisible to both halves.
Why it is worth recording rather than shrugging
The sweep's subject is whether an app-declarable optional load is host-anchored — a resolution-integrity property, not a style one. A load that leaves the population is not reported as unknowable; it is reported as nothing at all. So the guard's own failure mode is silence, which is the mode that does not announce itself.
Precedent — same class, other gates, all closed
Each was treated as worth fixing in the detector. This one is filed for the same reason and against the same shape.
Not proposed here
Whether to teach resolveIdentifier more spellings, to make the sweep fail loudly on an unresolvable specifier instead of dropping it (the shape that would close the class rather than widen it one hop at a time), or to derive the named list from something other than hand maintenance — is a decision for triage, not this filing. ⛔ Nothing in PR #12149 is asked to change; that PR's repair is correct on its own terms and is the reason this gap is now visible at all.
Measured against PR #12149's diff at 3ac21cf; the underlying class predates it (before that PR the resolver knew only const X = '<literal>', so a static-held spelling was already unresolvable).
Refs: #11614 · PR #12149 · #10645 · #10769.
Recording only — OBSERVATION class. ⛔ Not claimed, no assignee, no
domain:*, no type. Filed by thedomain:cliPM seat while reviewing PR #12149 (#11614). Dedupe run before filing: no open card covers this detector; the closest matches are the same class in other gates and are all closed (see Precedent).The mechanism
packages/cli/src/commands/serve-cluster-host-resolution.test.tssweepsserve.tsfor dynamic loads and asserts each app-declarable one is host-anchored. To do that it must resolve a specifier held in a variable, viaresolveIdentifier().⭐ The failure direction is the dangerous one: a specifier it cannot resolve drops OUT of
APP_DECLARABLE_LOADSrather than into it. An unrecognised spelling therefore does not fail the sweep — it silently removes that load from the population being judged, and every assertion downstream keeps passing over a smaller set.That is not hypothetical. It fired during #11614: moving the
@objectstack/organizationsspelling onto a class static made the specifier unresolvable, and the organizations load stopped being swept. PR #12149 repairs it correctly — in the detector, by resolving one hop further (const X = Serve.MEMBER→ that class'sstatic readonly MEMBER), which strictly widens the judged population and adds no allowlist, ratchet or baseline.What is still open after that repair
The repair teaches the resolver one further spelling. The class remains:
Serve., so a static on any other class or namespace is unresolvable;;;static readonlyon the same file.Any other spelling — a
let, a destructure, an object member, a re-exported constant, or passing the static straight into the call site with no intermediateconst— resolves toundefinedand takes its load quietly out of the sweep.⭐ A vacuity guard that COUNTS cannot detect a specific member disappearing from its population. The
> 20floor absorbed the organizations loss without a word; the named list is what caught it, and it covers four packages chosen by hand. A fifth load added tomorrow, spelled a way the resolver does not know, is invisible to both halves.Why it is worth recording rather than shrugging
The sweep's subject is whether an app-declarable optional load is host-anchored — a resolution-integrity property, not a style one. A load that leaves the population is not reported as unknowable; it is reported as nothing at all. So the guard's own failure mode is silence, which is the mode that does not announce itself.
Precedent — same class, other gates, all closed
const, so alet-held error code is reduced by nobody even when every branch is a literal #10918 — the dispatcher gate's resolver anchors onconst, so alet-held error code is reduced by nobody.check:dispatcher-error-vocabularystill cannot see a code held in a local ternary —sys-metadata-repository's live 403NOT_CREATABLE/NOT_OVERRIDABLE#9568 —check:dispatcher-error-vocabularycannot see a code held in a local ternary.check-engine-double-contractcannot read several local-binding and mock-initializer spellings, and one hid a live unguarded double.Each was treated as worth fixing in the detector. This one is filed for the same reason and against the same shape.
Not proposed here
Whether to teach
resolveIdentifiermore spellings, to make the sweep fail loudly on an unresolvable specifier instead of dropping it (the shape that would close the class rather than widen it one hop at a time), or to derive the named list from something other than hand maintenance — is a decision for triage, not this filing. ⛔ Nothing in PR #12149 is asked to change; that PR's repair is correct on its own terms and is the reason this gap is now visible at all.Measured against PR #12149's diff at
3ac21cf; the underlying class predates it (before that PR the resolver knew onlyconst X = '<literal>', so a static-held spelling was already unresolvable).Refs: #11614 · PR #12149 · #10645 · #10769.