Uh oh!
There was an error while loading. Please reload this page.
fix(cli,verify,dogfood): branch the multi-org remedy on the ABSENCE, not on one failure kind - #15158
Conversation
…three sites Red-first for #14270. #14041 added a third `HostImportFailureKind`, `declared-no-loadable-entry` — the app declares the package, the install delivered it, and the package's own `exports` names no runtime entry Node can load. Three consumers pick their remedy with a two-way branch written when only two kinds existed, so the third falls into the else leg and renders the DECLARE remedy at an operator who has already declared AND installed it. Each site gets a fixture carrying that kind and an assertion naming which arm fired. The verify and dogfood fixtures are real host roots on disk whose installed package publishes a `types`-only `exports` map; the CLI formatter takes the kind directly. Controls for `undeclared` and `declared-unresolvable` are untouched. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza
…not on one kind `declared-no-loadable-entry` (#14041) means the app declares the package, the install delivered it, and the package's own `exports` names no runtime entry Node can load. All three consumers picked their remedy with a two-way branch written when only two kinds existed, so that kind fell into the else leg and printed "declare it in the app's package.json and install" — at an operator who has already done both, and directly contradicting the importer's own message printed beside it. Each branch now asks "is the declaration the problem?": undeclared keeps the declare remedy, `declared-unresolvable` keeps the install remedy, and the third kind prescribes nothing and defers to the importer's message, which every one of the three sites already interpolates. No fourth remedy sentence is minted — the importer words the package-shape case once, and a local re-wording would be a second copy to drift. `packages/types` is untouched: the strings deferred to are #14278's. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza
…tion the app already has Patch for @objectstack/cli and @objectstack/verify — the two published packages whose `files` field ships the changed code. @objectstack/dogfood is `private: true` with no `files`, so it publishes nothing and takes no changeset entry. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza
…EST_DEBT `harness.host-resolution.test.ts` imports `./harness` without the `.js` extension, so under NodeNext the specifier does not resolve and every symbol it names is `any` — which makes a `.then` callback PARAMETER implicitly any. The two new cases used `.then(onFulfilled, onRejected)` and added 2 x TS7006, taking the package's TEST_DEBT entry from its recorded 3 to 5. That ledger is shrink-only, so the fix is the errors, not the ceiling. try/catch instead: a `const` initialised from an `any` call is not a diagnostic, only a parameter is. `check:type-check-debt --re-measure` now reports 17 entries re-measured, 217 raw errors, none above its recorded number. ⛔ Deliberately NOT the one-line `.js` fix that graduates the entry — that is a different card's, filed separately. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza
📓 Docs Drift CheckThis PR changes 2 package(s): 17 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 4 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 23 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 14487222e4b974a8531badcf2b4fea32385d0ed9 && git checkout 14487222e4b974a8531badcf2b4fea32385d0ed9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 4283b72a15f9c5ced0755a1b9c8aa53e5f8e16fa dd611c3856bb3e8f38dd736fefcf70410f69b9e4 && git checkout -B drift-repro 4283b72a15f9c5ced0755a1b9c8aa53e5f8e16fa && git merge --no-ff dd611c3856bb3e8f38dd736fefcf70410f69b9e4
node scripts/docs-audit/affected-docs.mjs --json 4283b72a15f9c5ced0755a1b9c8aa53e5f8e16fa
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#14270
What was wrong
HostImportFailureKindgot a third member in #14041 —declared-no-loadable-entry: the app declares the package, the install delivered it, and the package's ownexportsnames no runtime entry Node can load (atypes-only orbrowser-only publish, or an unexported subpath). Three consumers pick their remedy line with a two-way branch written when the type had exactly two members, so the third kind fell into the else leg and printed the declare remedy:packages/cli/src/commands/serve.tsformatOrganizationsInstallRemedypackages/verify/src/harness.tsbootStack's multi-tenant fatalpackages/qa/dogfood/test/enterprise-organizations.tsprobeOrganizations's refusal / skip reasonEach of those three also interpolates the importer's own message, which words the case correctly ("the package publishes no entry that Node can load … the remedy lives in the package"). So the bullet an operator reads first contradicted the diagnosis printed underneath it, and prescribed two actions that were already done and could not have helped. That is the confidently-wrong-verdict class #14041 removed one layer down, reintroduced by its callers.
The shape
Each branch now asks "is the declaration the problem?" rather than testing one kind:
undeclared⇒ declare remedy — unchangeddeclared-unresolvable⇒ install remedy — unchangeddeclared-no-loadable-entry⇒ prescribes nothing. It names the two things that are not the problem and defers to the importer's message, which every one of the three sites already prints.⛔ No fourth remedy sentence is minted. The importer words the package-shape case once, in the place that measured it; a local re-wording in three consumers would be three copies to drift. The deferral follows an idiom already in
serve.ts—formatOrganizationsMountFatal's "its message is the authority on the remedy; this CLI has no further detail to add" — and the same reasoningformatI18nLoadDiagnostic's docblock gives for interpolating only the kind TOKEN.An error carrying no kind never came from the host importer at all and keeps the declare-and-install arm it has always had. Narrowing that one is a different question about a different fact, and is not this branch's.
⛔ The seam with #14278 / PR #15047
packages/types/src/node.tsis not touched — verified on the diff:The strings this card defers to (
/publishes no entry/,/INSTALL problem/) are pinned by #14278's tests inpackages/types/src/node.test.ts. This PR changes the three callers' remedy bullets so they stop contradicting that message.Proof
Red-first, per formatter, naming which arm fired. Tests landed in
063d976b7a, implementation in78a0a7587f. Against the tests-only tree, all three sites reproduced the defect with a fixture carrying the third kind:The verify and dogfood fixtures are real host roots on disk — a real
node_modules, a real installed package whose manifest publishesexports: { ".": { types: "./index.d.ts" } }and nothing else, nothing mocked. Neither condition set resolves it (ESM_IMPORT_CONDITIONS/CJS_REQUIRE_CONDITIONS), so the importer classifies itdeclared-no-loadable-entryfor real rather than by injection.Reverse verification — the three formatters restored to their pre-implementation bytes, same suites re-run, then restored:
Restore proven by blob-hash equality against the HEAD blob for all three files, with
git status --porcelainempty andgit diff HEADclean — never by an editor exit code. The mutation ran undertrap … EXIT INT TERMrestoring absolute paths, and the restore leg usesgit checkout HEAD -- PATHrather than a baregit checkout -- PATH, since the mutation leg wrote the index too. No rebuild is involved on either leg: all three suites import their subject by relative source path (./serve.js,./harness,./enterprise-organizations.js), not through a packageexportsmap intodist.Controls, byte-unchanged, in the same runs. The
undeclaredanddeclared-unresolvablearms are pinned at all three sites and pass identically either side of the change — they are green on the mutated tree above and on the implemented tree. Two of those controls are new here: neither the verify suite nor the dogfood suite had adeclared-unresolvablefixture, so this PR adds one to each (declared, not installed) purely as a control.Green, at
dd611c3856:Gates
The union was derived at the final HEAD
dd611c3856on a clean tree withnode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands— 50 families (39 by path + 7 by change kind + 6 declared whole-tree, 2 reached both ways) — never from a hand-written path list. All 50 were run locally; each gate's exit code was captured before any pipe. 49 green first time.One was a real red, and it was mine.
pnpm check:type-check-debtreported:harness.host-resolution.test.tsimports./harnesswithout the.jsextension, so under NodeNext the specifier does not resolve and every symbol it names isany— which makes a.then(onFulfilled, onRejected)callback parameter implicitly any. My two new cases used that shape and added 2 × TS7006. TEST_DEBT is shrink-only, so the fix is the errors, not the ceiling:dd611c3856rewrites both as try/catch (aconstinitialised from ananycall is not a diagnostic; only a parameter is). ⛔ The one-line.jsfix that would graduate the entry is deliberately not taken here — different card, filed as #15145.Re-run at the final HEAD after that fix, each exit captured per gate:
Re-deriving the union at
dd611c3856returns the same 50 families — the ratchet fix implicated nothing new.Typecheck coverage — measured, not assumed
Measured with
tsc --noEmit --listFiles, because "typecheck is clean" says nothing about a file the program never read:packages/cli/src/commands/serve.tspackages/cli/tsconfig.jsonpackages/cli/src/commands/serve-organizations-message-spelling.test.tspackages/cli/tsconfig.json— itsinclude: ['src']reaches this file, since the pin lives undersrc/commands/, not undertest/. It is 0 files intsconfig.test.json, whose project istest/.packages/qa/dogfood/test/enterprise-organizations.tsand its testpackages/qa/dogfood/tsconfig.json(include: ['test/**/*'])packages/verify/src/harness.tspackages/verify/tsconfig.jsonpackages/verify/src/harness.host-resolution.test.tsexcludelists**/*.test.ts, and the package has nocheck:test-typecheckwiring. Its types are read only bycheck:type-check-debt --re-measure, which is what caught the +2 above. Filed as #15145.So:
pnpm --filter @objectstack/verify typecheckbeing green is not a statement about the test file this PR edits. The statement that covers it is the ratchet line quoted above.Changeset
Measured from each of the three packages' own
filesfield, because the answers differ:@objectstack/cli—files: ["dist", …],publishConfig.access: public⇒ published,src/commands/serve.tsships ⇒ in the changeset.@objectstack/verify—files: ["dist", …], published ⇒src/harness.tsships ⇒ in the changeset.@objectstack/dogfood—private: true, nofilesfield at all ⇒ publishes nothing ⇒ not in the changeset, and cannot be.Patch, not minor: operator-facing wording on a failure path, no API change. Nothing here removes or renames anything an author can write, so no ADR-0087 disposition marker is owed.
Out of scope, filed
packages/verifyhides its own tests from itstypecheckscript and carries frozen TEST_DEBT instead of thetsconfig.test.jsonwiring 23 other packages use #15145 —packages/verifyhides its own tests from itstypecheckscript and carries frozen TEST_DEBT instead of thetsconfig.test.jsonwiring 23 other packages use. Found through the gate red above.Fences
packages/types/src/node.ts,packages/rest/**,packages/spec/**andcontent/docs/releases/**are absent from the diff (asserted above).serve.ts:133's "keep the two in sync" comment and everyapps/objectos/apps/cloudcomment are untouched — 0 diff hits for that string.packages/verify/src/harness.tsmoved one remedy branch and nothing else in the ADR-0105bootStackharness.Draft on purpose: ⛔ not flipped ready, ⛔ auto-merge not armed — the PM does that.
🤖 Generated with Claude Code
https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza
Generated by Claude Code