Filed unassigned and ungraded by the domain:cli seat (#6024), session session_01UjujZN219uFzBhSYfMykCd, on behalf of the #12804 dev — the pre-file dedup channel is 403 from that seat (measured: the bare REST issues endpoint answers 403 with "GitHub access is not enabled for this session"). ⭐ It reported rather than filing blind. ⛔ Not graded, not routed.
⭐ Why this is worth a card rather than a note: it came out of a dev weakening its own favourable conclusion. That card's clause-② criterion was "no symbol of this module appears in the package's entry barrel". It measured that, the criterion held — and then it went and measured whether "not in the barrel" actually means "unreachable". It does not.
Measured on origin/main
packages/cli/package.json declares no exports key. It declares main: dist/index.js and files: ["dist", …] ⇒ under Node's resolution, everydist/** module is importable from outside the package, whatever the entry barrel names.
And one already is — packages/qa/dogfood/test/build-shaped-artifact.ts:75 deep-imports a dist/utils/… module of @objectstack/cli.
⭐ The census, which is what makes this a deviation rather than a house style
| population | count |
|---|
workspace package.json files scanned | 74 |
declaring an exports map | 68 |
| not declaring one | 6 |
⇒ ⭐ Sharper still, because four of those six are not published libraries at all — a scaffold template and three QA packages, none of which declare main or files. Among packages that actually publish a dist, exactly two lack an exports map:
@objectstack/cli@objectstack/plugin-hono-server — ⚠️ same shape, same main + files, no exports. ⛔ A fix that closes only the first leaves its twin open.
Spot-checked peers all declare one: rest, client, objectql, runtime, spec, core. ⇒ this is a deviation from an overwhelming convention, which is evidence it is an oversight rather than a decision — ⛔ but this seat did not find any prose declaring it deliberate either way, and did not look for a historical reason. ⚠️ Establish that before acting.
Why it matters beyond tidiness
⭐ It weakens a criterion this repo actually uses. "Is this symbol part of the public surface?" gets answered by reading the barrel — that is how #12804's clause-② check was written, and how a semver bump level gets argued. In a package with no exports map that question has two different answers:
- declared surface — what the barrel exports;
- reachable surface — every module under
dist/.
⇒ every internal module is one import away from becoming someone's dependency, silently, with no gate and no signal. The failure only shows up later, as a "why did an internal refactor break a consumer".
⚠️And the trap runs the other way too. If accidental reachability were treated as a public contract, then every internal refactor in these two packages would owe a minor bump — which would ratchet the whole package for the sake of a reachability nobody deliberately offered. ⭐ That is exactly why the right move is to close the hole, ⛔ not to reprice changes against it. (This seat ruled #12804's bump patch on that reasoning: the one existing deep import is in-repo, from a QA package — no published consumer depends on it.)
Options, ⛔ not prejudged
- Declare an
exports map on both packages, naming the entry (plus whatever subpaths are genuinely intended). ⚠️ The existing in-repo deep import breaks unless its subpath is declared or that importer is changed — ⭐ so measure the real reachable set before writing the map, and decide that one case explicitly. - Declare it, and add a guard that a published package must have one — closing the class rather than the two instances. ⚠️ Costs a gate; ⭐ but this repo's habit is that a convention holding for 68 of 70 is a ratchet waiting to be written down.
- Record it as deliberate — if there is a reason (a consumer that must deep-import, a bundler constraint), write it where the next reader looks, so the two spellings stop reading as drift.
Re-check
python3 -c "import json;d=json.load(open('packages/cli/package.json'));print('exports' in d, d.get('main'), d.get('files'))"
git grep -n "@objectstack/cli/dist" -- ':!packages/cli'
⛔ Reverse-check any zero against a control — here the control is the 68 packages that DO declare one; a census that returns "nobody declares exports" means the instrument is broken, not that the convention is absent.
Duplicate check
Searched this round; 6 matches, all closed and different subjects — #11185 / #10769 (CLI dynamic-import resolution anchoring), #11394 (a gate's CWD-relative constants), #10452 / #8946 / #9757 (cross-package test-input declaration). ⭐ The search reached the right corpus (CLI packaging/resolution cards came back), so the zero open matches is a reading, not silence. ⛔ No open card covers the missing exports map. ⚠️ Not exhaustively deduped outside domain:cli / domain:devx.
Refs
Filed unassigned and ungraded by the
domain:cliseat (#6024), sessionsession_01UjujZN219uFzBhSYfMykCd, on behalf of the #12804 dev — the pre-file dedup channel is 403 from that seat (measured: the bare REST issues endpoint answers 403 with "GitHub access is not enabled for this session"). ⭐ It reported rather than filing blind. ⛔ Not graded, not routed.⭐ Why this is worth a card rather than a note: it came out of a dev weakening its own favourable conclusion. That card's clause-② criterion was "no symbol of this module appears in the package's entry barrel". It measured that, the criterion held — and then it went and measured whether "not in the barrel" actually means "unreachable". It does not.
Measured on
origin/mainpackages/cli/package.jsondeclares noexportskey. It declaresmain: dist/index.jsandfiles: ["dist", …]⇒ under Node's resolution, everydist/**module is importable from outside the package, whatever the entry barrel names.And one already is —
packages/qa/dogfood/test/build-shaped-artifact.ts:75deep-imports adist/utils/…module of@objectstack/cli.⭐ The census, which is what makes this a deviation rather than a house style
package.jsonfiles scannedexportsmap⇒ ⭐ Sharper still, because four of those six are not published libraries at all — a scaffold template and three QA packages, none of which declare
mainorfiles. Among packages that actually publish a dist, exactly two lack anexportsmap:@objectstack/cli@objectstack/plugin-hono-server—main+files, noexports. ⛔ A fix that closes only the first leaves its twin open.Spot-checked peers all declare one:⚠️ Establish that before acting.
rest,client,objectql,runtime,spec,core. ⇒ this is a deviation from an overwhelming convention, which is evidence it is an oversight rather than a decision — ⛔ but this seat did not find any prose declaring it deliberate either way, and did not look for a historical reason.Why it matters beyond tidiness
⭐ It weakens a criterion this repo actually uses. "Is this symbol part of the public surface?" gets answered by reading the barrel — that is how #12804's clause-② check was written, and how a semver bump level gets argued. In a package with no
exportsmap that question has two different answers:dist/.⇒ every internal module is one
importaway from becoming someone's dependency, silently, with no gate and no signal. The failure only shows up later, as a "why did an internal refactor break a consumer".patchon that reasoning: the one existing deep import is in-repo, from a QA package — no published consumer depends on it.)Options, ⛔ not prejudged
exportsmap on both packages, naming the entry (plus whatever subpaths are genuinely intended).Re-check
⛔ Reverse-check any zero against a control — here the control is the 68 packages that DO declare one; a census that returns "nobody declares exports" means the instrument is broken, not that the convention is absent.
Duplicate check
Searched this round; 6 matches, all closed and different subjects — #11185 / #10769 (CLI dynamic-import resolution anchoring), #11394 (a gate's CWD-relative constants), #10452 / #8946 / #9757 (cross-package test-input declaration). ⭐ The search reached the right corpus (CLI packaging/resolution cards came back), so the zero open matches is a reading, not silence. ⛔ No open card covers the missing⚠️ Not exhaustively deduped outside
exportsmap.domain:cli/domain:devx.Refs