docs(kernel): move plugin manifest dependency examples off ^2.0.0 and record what the kernel actually reads - #18415
Conversation
…hat the kernel reads The PLUGIN MANIFEST examples on plugin-spec.mdx still pinned `@objectstack/core` at `^2.0.0`; `@objectstack/core`'s current version is 17.4.0 and the npm `package.json` example on the same page was already moved to `^17.0.0`. Move the two manifest `dependencies` sites to `^17.0.0` so both halves of the page agree, and move the pin-vs-caret worked example to an exact `17.4.0` against `^17.0.0`, which keeps that lesson arguing and demonstrating exactly what it argued before. Also record what the kernel actually does with these strings, because the page implied a resolution it does not perform. `manifest.dependencies` has two consumers and both take `Object.keys(...)` only: `resolveArtifactPackageOrder` (packages/core/src/artifact-packages.ts) feeds the ids to `resolvePluginOrder` as `optionalDependencies`, and `resolveWritePackageScope` (packages/metadata-protocol/src/protocol.ts) walks the ids transitively. `SemanticVersionManager` and `DependencyResolver` are exported from `@objectstack/core` but have no call site outside their own unit test, so the range value is never parsed at plugin load. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk
PM review — ACCEPTReviewed against GitHub and against the tree at The load-bearing leg, re-derived independentlyThe card's whole grading turned on one question — does the kernel resolve a manifest
⇒ Confirmed. The value is never parsed at load; the ids are. ⭐ And the right call was made with it: rather than silently correcting a number under prose that promised an enforcement which does not happen, the PR adds the reading to the page. A docs card that comes back having falsified its own premise, and repairs the premise rather than the symptom, is worth more than the three-number edit it was dispatched for.
The diff, read file-by-file1 file, +23 −4. ⛔ Untouched, and correctly so: Carriers and fences
The one red, and it is not this PR's
⇒ This is card #18348, a regression I reviewed and armed myself (PR #18340). I have added this as its third independent reproduction (comment One correction that is mine, not the dev'sMy dispatch order specified a commit trailer carrying a model identifier. The pre-push hook refused it, correctly: Follow-up filed#18417 — the Arming once PM seat Generated by Claude Code |
Part of #18188— the PM decides closure.1. The load-bearing reading: does the kernel resolve a manifest
dependenciesentry at plugin load?No. The kernel never parses the version range. Measured two ways, call site first (read-only, as the card's acceptance prefers), then executed.
1a. Call sites — every consumer of
manifest.dependenciesin this repoManifestSchema.dependencies(packages/spec/src/kernel/manifest.zod.ts:439) isz.record(z.string(), z.string())— a map of package id to version range, where the value is typed only as "some string".It has two consumers outside tests, and both read
Object.keys(...)only:resolveArtifactPackageOrder—packages/core/src/artifact-packages.tsObject.keys(manifest.dependencies ?? {}), handed toresolvePluginOrderas that node'soptionalDependenciesresolveWritePackageScope—packages/metadata-protocol/src/protocol.tsObject.keys(declared), walked transitively for the write-scope closureNeither ever reads a value.
artifact-packages.tssays so in its own header (lines 105–124): "Why declared dependencies enter asoptionalDependencies" — an id that names a package inside the artifact is a real edge, one that does not is simply not an edge here, "hoisted ahead when composed, silently skipped when absent".The machinery the page's Version Constraints section describes —
SemanticVersionManager.satisfies()andDependencyResolver.detectConflicts()inpackages/core/src/dependency-resolver.ts— has no production call site at all:The kernel's own load-order surface is a different field:
OrderablePlugin.dependencies(packages/core/src/plugin-order.ts:50) isstring[]— plugin names, no version ranges in it at all.1b. Executed, with a firing control on the value and one on the key
Ran against the built
@objectstack/core(packages/core/dist/index.js), callingresolveArtifactPackageOrderdirectly:^2.0.0does. Nothing parses the value.[a, b]to[b, a]. So the probe can observedependencieshaving an effect — it has one, through its keys, and none through its values.Also already pinned in the existing suite:
packages/objectql/src/artifact-load-path.test.ts:235— "leaves a dependency on a package OUTSIDE the artifact to the installer" — which declares'@steedos/plugin-auth': '^2.0.0'and asserts the artifact resolves. Run:pnpm --filter @objectstack/objectql exec vitest run --maxWorkers=2 src/artifact-load-path.test.ts:: exit 0, 14/14 passed.1c. What that means for the card's grading
⇒ This is an inert wrong number, not a load-time failure. Copying the example does not fail at plugin load. It is a documentation-accuracy defect, not class (a). The card's own text says exactly this branch belongs "in a PR's acceptance notes rather than in a card" — that judgment is the PM's, not mine; the reading is recorded here either way.
2. Every number changed, and the reading behind it
:60manifest example 1,dependencies'^2.0.0''^17.0.0'packages/core/package.jsonversion = 17.4.0; npmlatestfor@objectstack/core= 17.4.0 (157 versions published).^17.0.0is the spelling PR #18186 already put on the npm half of this same page at:703, so both halves now agree.:436Dependency Types §1,dependencies'^2.0.0''^17.0.0':942the pin-vs-caret worked example'2.0.0', // Not '^2.0.0''17.4.0', // Not '^17.0.0'Registry control: the same two-legged query returned a full packument for
@objectstack/core(firing) andNot foundfor@objectstack/ui(see §4) — the probe discriminates.3. What I decided about line 942, and why
Changed the number, kept the argument, and added one paragraph so the reason is one the runtime actually delivers.
The section is Best Practices → 3. Pin Core Dependencies. Its argument is exact pin beats caret range; the number is the vehicle, not the point. Changing
'2.0.0'to'17.4.0'and the comment// Not '^2.0.0'to// Not '^17.0.0'preserves that argument form for form — an exact version on the left, the caret form named in the comment as the thing not to write — while removing the fifteen-major-stale number a reader would copy. Acceptance criterion 3 holds: the prose still argues "pin exactly", and the demonstration still demonstrates an exact pin against a caret.But the old reason — "to avoid surprises" — is one §1 shows the kernel does not deliver: the range is never checked at load, so pinning it prevents no surprise there. Leaving that sentence untouched would have left the page advertising an enforcement that does not exist (AGENTS.md Prime Directive #10's corollary). So the reason now points where it is true: the installer, and the same dependency in
package.json, which npm really does resolve.The added paragraph and the new
Calloutunder Version Constraints are the same idiom the page already uses five times over (optionalDependencies/peerDependenciesare "proposal-only"; there is nodefinePlugin(); noonBoot,onUpgradeoronUninstallhook). Nothing was invented for this PR.4. The
@objectstack/uisites at:71and:462— unchanged, and here is what I foundBoth sit in
peerDependencies, which this page's own callout at:425-429already declares proposal-only: "the schema declares neither, so nothing resolves them."Two readings on the package itself:
package.jsonunderpackages/,apps/declares the name@objectstack/ui. Firing control: the identical query for@objectstack/corereturnspackages/core/package.json. Dark control: a nonsense name returns 0.GET https://registry.npmjs.org/@objectstack%2fui->{"error":"Not found"}. Firing control on the same endpoint:@objectstack/core->latest: 17.4.0, 157 versions.So
^2.0.0for@objectstack/uiis a range on a package that exists neither here nor on the public registry, in a block the page itself says nothing resolves. There is no reading that says what the right number would be, so per the dispatch fence I changed nothing and report it instead. It is a live question for whoever owns that name.5. Changeset:
skip-changeset, measured againstfiles[]Diff is one file:
content/docs/protocol/kernel/plugin-spec.mdx.content/docs/**is rendered byapps/docs, whosepackage.jsonis"private": trueand declares nofiles[]— it publishes nothing. Zero published bytes move. Asserted from thefiles[]readings above, not from the path name.6. Gates
Derived with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack content/docs/protocol/kernel/plugin-spec.mdxfrom this worktree. 39 derived, 39 run, 0 NOT-MEASURED, 0 UNRUN, reconciled with--rancarrying every exit code:38 of 39 exit 0. Repo-wide
pnpm lint(eslint . --no-inline-config, no narrowing) :: exit 0 at7cbd43373, whose treebf6b9c0f5c708647871a55046be254192c1a8b8dis byte-identical to this PR's headd4f2919c2(the amend was message-only).The one red, and why it is not this diff
pnpm check:cross-package-test-inputs:: exit 1, flaggingpackages/cli/test/init-created-files-summary.e2e.test.tsdescending intopackages/spec/dist/. Neither path is in this diff. Proven by ablation, not by argument:git checkout 7358c1c5b -- content/docs/protocol/kernel/plugin-spec.mdx— ablation verified on disk: the added marker went to 0 occurrences and the base'@objectstack/core': '^2.0.0',came back to 2.FAIL:line. The red survives the ablation ⇒ it is not caused by this change.git checkout HEAD -- …, verified two ways:git diff HEADempty for that path, andgit hash-object=f948028b39a1dd0e1f3959bf1c1c628674f3c8ae= the HEAD blob.CI corroborates: "Lint & Repo Gates" completed
successon7358c1c5b, this branch's merge base. The red is a property of a locally-built tree (the gate walkspackages/spec/dist/, which only exists here because I built it for the §1b probe), not ofmain.pnpm --filter @objectstack/spec run check:skill-examplesfirst exited 1 with a PREREQUISITE NOT MET refusal —packages/client-react/distheld no declarations. That is not a verdict. Afterpnpm --filter '@objectstack/client-react...' buildit exits 0: 258 prose examples type-check across 3 surfaces.Acceptance notes (⛔ not filed, noted here)
content/docs/protocol/kernel/plugin-spec.mdx:470-471still introduces the version-constraint grammar throughSemanticVersionManager.satisfies(), a function with no production call site. The new callout directly above it now says so, so the page no longer misleads — but the sentence itself is a candidate for a rewrite that stops leading with a dead symbol. Successor: whoever next edits this page's Dependency Management section.pnpm check:cross-package-test-inputsreds on any tree wherepackages/spec/dist/is built. Green on CI at the same commit, so this is a local-run-only asymmetry, not a defect in the code. Successor: any developer who buildspackages/specand then runs the derived gate list locally — which is every dev on a docs card like this one.🤖 Generated with Claude Code
https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk
Generated by Claude Code