Repository navigation
feat(install): check a package's declared dependencies before install — 422 DEPENDENCY_MISSING at the three install acts (ADR-0141 D2) - #25986
Conversation
…ncy predicate, wired at the protocol primitive and POST /api/v1/packages Claude-Session: https://claude.ai/code/session_01NndsH9Qm7k6AS2i2fWbjNA Co-authored-by: Claude <noreply@anthropic.com>
…ger row, docs and changeset Claude-Session: https://claude.ai/code/session_01NndsH9Qm7k6AS2i2fWbjNA Co-authored-by: Claude <noreply@anthropic.com>
…ger row Claude-Session: https://claude.ai/code/session_01NndsH9Qm7k6AS2i2fWbjNA Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NndsH9Qm7k6AS2i2fWbjNA Co-authored-by: Claude <noreply@anthropic.com>
… doc roster Claude-Session: https://claude.ai/code/session_01NndsH9Qm7k6AS2i2fWbjNA Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 5 package(s): 21 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 153 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 682ada6a125a90973b1925604fabee4e0b8418cd && git checkout 682ada6a125a90973b1925604fabee4e0b8418cd
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d1663ba1826c338861c3b92c117346322a6af8fa 12daab9542fa8ea76bdcce4d3650b7b6da5edcc2 && git checkout -B drift-repro d1663ba1826c338861c3b92c117346322a6af8fa && git merge --no-ff 12daab9542fa8ea76bdcce4d3650b7b6da5edcc2
node scripts/docs-audit/affected-docs.mjs --json d1663ba1826c338861c3b92c117346322a6af8fa
|
Contract reviewServed-tier: ① Derived judgmentsCI state at review time (2026-10-26T08:18Z, 46 check-runs on the head). 32 concluded A. The accept set at the three install acts — right. The one predicate Refused Two consequences of fail-closed worth stating, both correct under the ADR and both documented on the new docs page, neither blocking: (i) B. Presence-only satisfaction by a composed app — right. C. D1 verdicts unchanged by the shared peel — right, by construction. The three extracted helpers are byte-equivalent to the code they replace: D. Fail-closed on an out-of-grammar range — right. E. The ledger row and the new metadata-core exports — right. F. The two out-of-surface files. G. Claims I could not verify from the inputs. The PR body's HotCRM paragraph (three modules declaring ② Semver levelLevels. The changeset introduces The declaration line. The dev's Metadata-protocol: Carriers. The changeset carries the ③ Boundary flagsOpen question 6a ( Open question 3 (the declaration line). Ruled above: keep Dogfood failure Deviations from the claim. (1) The declaration line, ruled above. (2) Two files outside the claim's listed surface: the roster entry is machine-required; the anchor is convention-required and correct (① F). Neither is a Out-of-scope findings and the split. Items 4 and 5 (D5 co-owner source and rehydrate order) and First readings 2 and 6 are deferred to the next PR on #25792; the docs page says D5 is not delivered, and the Sharing-a-namespace section states today's Two non-blocking notes for the D5 PR, from ① A and ① B: the CI still owed at review time. Test Core shards 1, 3, 4, 5, 7 and Implemented-by: VERDICT: PASS Generated by Claude Code |
席位核对 · ACCEPT ·
|
| item | reading |
|---|---|
| Shape | draft, base main, Part of #25792 (items 1, 2, 7: ADR-0141 D2), Clause-②: yes (narrowing): the dev's measurement overrides the claim's no (narrowing …), as the review confirms (a new ledger code and new metadata-core exports widen one surface while install acceptance narrows), Metadata-protocol: none, patch on the moved packages with the BREAKING banner. |
| Files (18, +1500/−42) | the D2 check at the three install acts (protocol installPackage, the fallback arm of POST /api/v1/packages, install-local on both branches), one fail-closed range reader sharing the D1 comparator peel (D1 verdicts unchanged), the ledger row (the only packages/spec/** edit), docs, an ADR anchor and the docs-audit roster entry. Not governed (check-governed-merges --pr 25986). |
| Fix | an install with an unmet manifest.dependencies entry answers 422 DEPENDENCY_MISSING, one issue and one unmet fact per entry; a composed app counts as delivered (presence only); HotCRM passes unchanged. |
| Evidence | red first, four ablations, 127/127 derived gates reconciled (report). The dogfood authz-probe-blind-spot red is main's own since #25956 (census file and i18n.ts byte-identical to main). |
| Carried | items 4 and 5 (D5) are the next PR on #25792, with the review's two notes (the found label on a union with one unreadable alternative; the artifact-wrapper id in deliveredIds). Items 6a/6b (retiring PackageDependencySchema / optional and AppManifestSchema.dependencies) are not conditions of this PR and go to the maintainer. |
| Landing | 「按照 pm-dispatch 规则复核过的都授权」 (Leehom, 2026-10-25T11:35Z): ready + auto-merge through the relay; auto-merge waits for the last Test Core shards. The card stays open (Part of). |
Generated by Claude Code
Requested by Leehom · project thread
Part of #25792
Clause-②: yes (narrowing)
Metadata-protocol: none
Summary
ADR-0141 D2: installing a package now checks its own declared
manifest.dependenciesfirst. Every entry must name a package installed at amanifest.versioninside its range, or one the same artifact delivers (presence only). Otherwise the whole install is refused422 DEPENDENCY_MISSINGbefore anything is registered, written or synced, with oneAppCompatibilityCheckissue (category: 'dependency_missing') and oneunmetfact per entry, all in one answer.packages/metadata-core/src/protocol-handshake.ts).rangeAdmitsVersion(range, version)reads npm-style ranges at exact-version granularity (^ ~ >= > <= < =, exact,x/*, hyphen, space/comma intersection,||). It shares the comparator peel, hyphen split and length cap with the D1 major-only reader (rangeAdmitsMajor), whose verdicts are unchanged. Unlike D1, D2 fails closed: an unreadable range (latestincluded) or a non-MAJOR.MINOR.PATCHinstalled version never satisfies.checkDeclaredDependencies,assertDeclaredDependencies,DependencyMissingError(code+status422, branded like the protocol-incompatible error) anddependencyMissingAnswer, so every door answers the same envelope.installPackage(packages/metadata-protocol/src/protocol-package-install.ts), which is also the composed arm ofPOST /api/v1/packages;POST /api/v1/packages(packages/runtime/src/domains/packages.ts);POST /api/v1/marketplace/install-local, inline-manifest and cloud-snapshot branches (packages/cloud-connection/src/marketplace-install-local-plugin.ts). A multi-package artifact judges eachpackages[].manifest, with the artifact's own ids counted as delivered.kernel:readyrehydrate re-registration,PATCH /api/v1/packages/:id, and uninstall or upgrade of a dependency.DEPENDENCY_MISSINGis registered under@objectstack/metadata-coreinpackages/spec/src/api/error-code-ledger.zod.ts(the onlypackages/spec/**edit); the reference pages are regenerated.content/docs/deployment/app-dependencies.mdxcovers how to declare a dependency, the grammar, what install checks, the refusal and itsfoundvalues, and what is not checked. Theplugin-spec.mdx"Version Constraints" callout is corrected to name the install check as a consumer.Declarations
Clause-②: yes (narrowing). The diff adds public surface (a ledger code and new@objectstack/metadata-coreexports) and narrows install acceptance (a package with an unmet dependency used to install). The changeset ispatch, with a BREAKING banner, the "What changes for you" section and an ADR-0087not-required (no-migration-prescription)marker.Metadata-protocol: none. No key, accepted value or metadata type changes in what an author can write.manifest.dependenciesstill parses every value it parsed before. A value outside the grammar, such aslatest, is now refused at install, which is a run-time refusal and not a parse change (precedent #25954, #25964). This line is borderline, so the reviewer may want to confirm it.HotCRM
At hotcrm
d91c7b0, service, marketing and revenue each declaredependencies: { 'app.objectstack.hotcrm': '^6.0.0' }, and the app is6.0.0. They are composed into one artifact, so the check treats the app as delivered (presence only) and they pass unchanged. A module shipped standalone also passes, because^6.0.0admits6.0.0.Tests
Red first: the new metadata-protocol suite failed on main (2 failures) and passes after wiring. The new suites:
protocol-handshake-dependencies.test.tsprotocol-package-install-dependencies.test.tspackages-install-dependency-missing.test.ts(composed and fallback arms)marketplace-install-local-dependency-missing.test.ts(inline, cloud snapshot, multi-package, rehydrate not checked)Package suites on the merged head: metadata-core, metadata-protocol, cloud-connection and spec typecheck + test exit 0. Runtime typecheck exits 0, and its tests pass (448 files, 7188 passed, 19 skipped).
Ablations (
scripts/ablation-replace.mjs, each restore checked against HEAD):packages.tsGates:
node scripts/pm/dispatch-gates.mjs --ranaccounts for all 127 derived families, all exit 0. Three needed a prerequisite (unshallow, full build) and one found a real miss, the doc roster entry, which is now fixed. Scopedeslinton the touched packages exits 0, andcheck:nul-bytesexits 0. The gates ran atb1ed3d7420, before the last merge of main. Package typecheck and tests, the full build, and the full dogfood ran on the head12daab9542.Full dogfood on
12daab9542: 2522 passed, 18 skipped, 1 failed. The one failure isauthz-probe-blind-spot.test.ts, thepackages/runtime/src/domains/i18n.tsrow:handleI18nRequestcount expected 3, got 4. It is red on main too. Neither that file nor the census differs fromorigin/main, and the count moved with #25956. This PR does not touch it.Acceptance notes
ui/**edit. Item 6 (the fate ofAppManifestSchema/PackageDependencySchema) is reported as a decision on ADR-0141 runtime: pre-install dependency check (dependency_missing, 422) at the three install doors, plus dependency-declared namespace co-owners and the dependency-ordered rehydrate #25792, with no spec key change.dependency_missing, 422) at the three install doors, plus dependency-declared namespace co-owners and the dependency-ordered rehydrate #25792 remains open for D5.🤖 Generated with Claude Code
https://claude.ai/code/session_01NndsH9Qm7k6AS2i2fWbjNA
Generated by Claude Code