Repository navigation
docs(adr): ADR-0141 — an app may depend on another installed app: declared in manifest.dependencies, checked once before install, co-owning the namespace it shares - #25717
Conversation
…lared in manifest.dependencies, checked once before install, co-owning the namespace it shares Records the two maintainer rulings on the inter-app dependency decision (B′ 6369704387, namespace letter A 6377678758): declaration and reference stay the existing manifest key and the save door's closure; a pre-install check (installed, in range, else a clear refusal) fills the reserved dependency_missing category; uninstall protection, upgrade interception, topological/cascade install, the owner-side publish marker and marketplace version negotiation are deferred by name; permissions are unchanged; a declared dependency is one-way namespace co-ownership with the ADR-0130 D3 uniqueness check spanning it. ADR-0028 D4 is rewritten to "no cross-app reference without a declared dependency"; ADR-0019 gains an additive Related line; ADR-0130 gains an addendum moving D8's deferral end to marketplace publishing and marking its #14488 line stale. Claude-Session: https://claude.ai/code/session_01HwUcNzGjPhV6xtYkJ469qK Co-authored-by: Claude <noreply@anthropic.com>
…-anchors) Claude-Session: https://claude.ai/code/session_01HwUcNzGjPhV6xtYkJ469qK Co-authored-by: Claude <noreply@anthropic.com>
维护者速读改了什么 为什么改 风险与代价(含回滚) 席位意见 你要做的 席位核对Seat
|
… D2 placement and D5 relation restated, neighbour records re-amended WIP: full re-drafting pass over the first draft. Every factual claim re-read at the branch base; the maintainer's five verbatim lines carried whole; D2 names the three install acts and the three re-registration paths; D5 defines the co-owner relation once and states what reads it; D4 gains the ADR-0086 D4 discriminator question; ADR-0130's addendum records that #14488's premise sentence moved while its disposition stands. Claude-Session: https://claude.ai/code/session_01HwUcNzGjPhV6xtYkJ469qK Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs read: card #25403 body and all 7 comments (rulings ① Derived judgmentsA. Claims about today's code — each checked at
B. Claims about neighbouring records — each checked at
C. Decisions against the two rulings. Ruling Ruling Nothing the rulings deferred or rejected is decided here: every D3 item is a non-build with a trigger; the owner/publisher field stays deferred; renaming stays refused; permissions do not move. D. Where the drafter decided and the rulings were silent. The ADR lists four as merge questions, all four verified as real gaps the rulings left: (1) a dependency delivered by the same artifact counts as present — necessary, because E. The three neighbour edits. ADR-0028: five hunks and no more — the status line gains one "Amended" entry; TL;DR item 4, D4 (heading, a blockquote preserving the original sentence, body), D6's author-time bullet and leak-point 3 are rewritten; a grep of the head's 0028 finds no leftover "sandboxed"/"forbidden cross-app" sentence; the record stays Deferred. ADR-0019: one additive F. Check-runs on the head (read, not run). All 44 completed, none in progress. Required contexts: ② Semver level
③ Boundary flagsThe four drafter judgements the ADR lists for the merge decision:
Dev flags in the reports on #25403:
Nothing is escalated: no ruling is contradicted, nothing deferred is built, and the governed edits stay inside what the rulings ordered. Nits for the seat (none moves the verdict): ⑤ the 「目前的要求是发布物是完整安装的,没有设计单独安装其中一个模块的需求」 quotation (ADR-0141 D6; ADR-0130 addendum) drops an interior space and the full stop present in Implemented-by: VERDICT: PASS |
…cy_missing population, uninstall-refusal wording, D2 tense in ADR-0028, merge questions 5–10 Contract review PASS at 247fe2f named five nits; all five land here. The quotation from ruling 5645322259 is copied from the comment body (interior space and full stop restored) in ADR-0141 D6 and ADR-0130's addendum; the dependency_missing population names the three tracked files only; D3's preamble says what the ADR-0029 refusal is the only instance of; ADR-0028 D4 says D2 decides, not refuses, since D2 is unbuilt; the merge-decision list carries the drafter's six further choices (422, fail-closed parse vs the handshake's warn-and-admit, required entries, own entries only, the D8 conversion's sizing, the 5645322259 reading). Claude-Session: https://claude.ai/code/session_01HwUcNzGjPhV6xtYkJ469qK Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Delta review on top of the ① Derived judgmentsEach changed hunk, judged.
Rulings. No hunk moves a decision. Ruling Check-runs on ② Semver levelUnchanged from the reviewed head: ③ Boundary flagsThe ten drafter judgements the ADR now lists for the merge decision — items 1–4 as judged in comment Dev claims on this round (coordinator relay; the dev posted no new card comment) — each checked: byte-exact quote in 0141 D6 and the 0130 addendum: verified; Nothing escalated. No defect found in this delta. Implemented-by: VERDICT: PASS |
维护者速读改了什么 为什么改 风险与代价(含回滚) 席位意见 你要做的 席位核对Seat
|
Eight changes from the independent review the maintainer accepted in full: 1. D2 second arm: a dependency the same artifact delivers is judged for presence only (packages carry their own manifest.version; the artifact is one tested unit); no warning at install, the in-artifact range mismatch is os lint's. TL;DR 3, merge question 1. 2. D5 read-from-either-end: the stronger reason — installPackage refuses any sitting owner outside the co-owner set, so HotCRM could never be upgraded once a module sits in crm; the free upgrade guard recorded. 3. D5 sibling arm: a sitting owner that declares the same parent (holding the namespace) is a co-owner, read from either end, judged by the D3 uniqueness check too; keeps the second module independently installable. 4. D2 fail-closed paired with a stated range grammar (the tree's two readers accept less than npm's) and an author-side os lint/os validate rule; merge question 6, Execution plan card 2. 5. D8 item 1 (record-to-array snapshot conversion) is its own card, first step the control-plane reader; snapshots keep the record shape meanwhile. 6. Ruling 5645322259 read as unchanged by B′ (artifact installs whole; a standalone module is another artifact; B′ takes the carve-out the ruling named) in D6, Verification notes, merge question 10, 0130's addendum. 7. PATCH /api/v1/packages/:id: the post-install dependencies edit is deferred by name in D3 with interim state and trigger; merge question 11. 8. D7: the carved-out module is published as type: 'app' (ADR-0019 D2, isConsumerInstallable); HotCRM side not re-verified; ADR-0019 D1/D4 interaction noted in one sentence; Open item 6. Claude-Session: https://claude.ai/code/session_01HwUcNzGjPhV6xtYkJ469qK Co-authored-by: Claude <noreply@anthropic.com>
F1 D5/D7: the depended-on app's companions must declare it — the registry
keeps no artifact record after install (InstalledPackageSchema, scope
not persisted), so a standalone module meets the app's other packages
as foreign sitting owners and the app's next upgrade meets the module;
stated as a precondition, Open item 7 for the HotCRM lane.
F2 D5: hydrate order is a requirement — the sys_packages hydrate and the
install-local ledger register in list/readdir order and a refused row
stays absent for the process; decided: both paths sort their batch by
manifest.dependencies through resolvePluginOrder (ADR-0116's sorter);
merge question 12; Open item 2 narrowed to cross-path order.
F3 D2: three range readers, not two — rangeAdmitsMajor reads the full
grammar at major granularity, parseRangeFloor the floor only,
SemanticVersionManager full versions over a narrow grammar with no
caller; the handshake family in metadata-core is the reader to extend.
F4 0130 addendum (and header pointer) name the sibling arm.
F5 DUPLICATE_ARTIFACT_OBJECT_NAME keeps its code; message worded per
co-ownership source.
F6 PATCH door reaches only authored packages (WRITABLE_PACKAGE_REQUIRED,
ADR-0070 D2) and can delete the entry that is D5's proof; D3 row and
merge question 11.
F7 Ruling 5645322259 read in two halves: artifact-installs-whole unchanged;
the need the second half denied is met by another artifact.
F8 Amends/D6 name 0130's header pointer line beside the addendum.
F9 D7: HotCRM-side hooks and permission-set preconditions, not re-verified.
F10 Merge question 5 weighs the 409 precedents and keeps 422.
F11 kernel:ready seals folded into the hydrate-order requirement.
Claude-Session: https://claude.ai/code/session_01HwUcNzGjPhV6xtYkJ469qK
Co-authored-by: Claude <noreply@anthropic.com>
N1 D5 hydrate sort: a cycle in a batch degrades its cyclic members to source order with a logged refusal and never throws out of the hydrate; resolveArtifactPackageEntries' artifact-level cycle throw is not inherited (both hydrate paths stay per-entry tolerant). Merge question 12 and Execution plan card 1 say the same. N2 Install-local sort nodes are ledger entries: a declared id resolves to the entry carrying that package; ids inside one entry are not edges. N3 D2 reader: the exact-version reader is a sibling function in the handshake's module; the handshake's verdicts and ADR-0087 D1's never-false-rejection contract are unchanged. N4 Runtime card restated as S (the ruling's XS–S cited as the ruling's estimate for the check alone) in the Execution plan, merge question 9 and Consequences. Claude-Session: https://claude.ai/code/session_01HwUcNzGjPhV6xtYkJ469qK Co-authored-by: Claude <noreply@anthropic.com>
Independent full review (owner's order, 2026-10-25)Head reviewed last: The project owner ordered, on 2026-10-25, that spec, skills and ADR changes owe a complete review at
Every code claim the record makes was checked at Generated by Claude Code |
Contract reviewServed-tier: Third record on this PR, a delta review on top of comments ① Derived judgmentsA. Every changed hunk, judged against the code and the rulings. ADR-0141 header. "Amends" now names the 0130 status-header pointer line "the form 0130's 2026-09-22 line set" — right, 0130's header carries such lines for its 2026-09-02 and 2026-09-22 addenda. "Consumers" adds TL;DR 3–5. Co-delivered dependency "presence only: the artifact is one tested unit"; the range grammar "stated by the runtime card"; author tooling reports an unreadable range before install; item 4 adds the sibling arm and its two conditions; item 5 adds a sixth deferred item (a post-install edit of Context §1 — Context §1 — in-repo population. D2 arm 2 — presence only for a co-delivered dependency; range for an installed record. The reasoning rests on the facts above (each package carries its own D2 — "The range reader, and the grammar it must state". Verified claim by claim at D2 — re-registration paragraph. Adds "The order a boot registers in is D5's requirement, because the namespace gate does run there." Right: the D2 — "Nor the manifest edit". Verified at D3 table, sixth row. Interim state and trigger follow from the PATCH facts above; "a deleted entry removes D5's proof of co-ownership: the next related registration … is refused at the namespace gate naming the other package" is correct under D5's relation. Nothing the rulings chose is removed; the five ruled deferrals stand. Right. D5 — sibling arm. Definition is precise (S and P each directly declare one installed Q whose namespace is N). The justification is correct against the gate: D5 — read from either end, with the upgrade argument. The new reason is correct and stronger than round one's: once revenue sits in D5 — error message per co-ownership source. D5 — companions must declare the app. Verified: D5 — hydrate order is a requirement (merge question 12). Verified: D6 — ADR-0130 bullet and the #14488 reading. Now a two-halves reading: "an artifact installs whole" unchanged (ADR-0130 D2 — verified: "The consumer still installs one thing … the App you installed brought its modules"); the need the second half denied is met by another artifact, the carve-out path ruling D7 — D8. The conversion moves to its own card whose first step reads the control plane's parser of Consequences, Open items 2/6/7, Verification notes, Execution plan (five items; runtime card S, author-side XS, snapshot S), References. Consistent with the decisions above; References add Merge questions 1–12. Items 1, 2, 3, 4, 5, 6, 9, 10 rewritten; 11 and 12 new. Item 5's precedents verified: ADR-0130, three hunks. Status-header pointer line gains "(and its declared siblings' — ADR-0141 D5)"; the D8 paragraph of the addendum adds "plus siblings under one declared parent that holds the namespace, as ADR-0141 D5 defines"; the #14488 paragraph is rewritten to the two-halves reading with the quotation byte-identical. All three say only what ADR-0141 D5/D6 say; zero deletions of original text anywhere in 0130; D1–D8, §1–§8 and both earlier addenda untouched. Right. B. The rulings. Ruling C. New drafter decisions, each named. (1) co-delivered presence-only → merge question 1; (2) stated grammar, handshake-family reader, author-side rule → 6; (3) sibling arm → 4; (4) PATCH deferral as a sixth D3 item → 11; (5) per-path hydrate sort with cycle fallback → 12; (6) D8 conversion as its own card, runtime card S → 9 and Execution plan; (7) 422 against the 409 precedents → 5; (8) two-halves reading of D. Check-runs on ② Semver levelUnchanged: ③ Boundary flagsThe twelve drafter judgements the ADR lists for the merge decision:
Independent review PR body vs head: 维护者速读, 你要做的 (twelve items with the HotCRM precondition), the four "What changed" rows, the Measurements bullets (range readers, PATCH, hydrate order, example versions) and the Acceptance notes all describe Nothing escalated. Nits for the seat, none moving the verdict: ⑫ PR body, Gates paragraph: "+637 / −16" should read "+851 / −16" for head Implemented-by: VERDICT: PASS |
维护者速读改了什么 为什么改 风险与代价(含回滚) 席位意见
你要做的 席位核对Seat
|
Requested by 田騏 · project thread
Fixes #25403
Clause-②: no
Metadata-protocol: none
Docs-only, Tier H (
docs/adr/**): the PR stays draft and the maintainer merges it. Records the two maintainer rulings on #25403 — B′ (6369704387) and the namespace letter A (6377678758) — as a new ADR, and makes the three neighbouring records say the same thing.维护者速读(草稿)
改了什么
新增 ADR-0141,把「应用 B 可以声明依赖应用 A」写成契约:声明沿用
manifest.dependencies,引用沿用保存门的依赖闭包;唯一新增的关口是安装前检查(依赖已装、版本在范围内,同一发布物内送达的依赖只看在不在,否则拒绝并说明缺什么,填上预留的dependency_missing;检查支持的版本范围文法由运行时卡写明,作者侧os lint/os validate先于安装报出读不懂的范围);声明了依赖且命名空间相同的包,在安装门上与被依赖方共有命名空间(单向,门禁从任一端读取;同一被依赖方下的两个模块互为同门,不必互相声明),前提是主应用发布物里共用该命名空间的其余包也直接声明主应用(注册表安装后不记录包来自哪个发布物);开机重注册的两条路径各自按manifest.dependencies排序后再注册,对象名唯一检查跨发布物同样生效。卸载保护、升级拦截、拓扑/连带安装、被引用方发布标记、市场版本协商、安装后通过 PATCH 改动dependencies的判定,逐项写为推迟项;D8 的快照记录到数组转换另立一张卡;权限不变。另外三处配套:ADR-0028 D4 改为「未声明依赖不得跨应用引用」;ADR-0019 只加一行交叉引用(原文不改);ADR-0130 加附录,D8 的推迟改为「到市场发布时结束」,并标注它关于 #14488 的那一行已过时。为什么改
你在 #25403 批了 B′ 和命名空间 A。HotCRM 想把 revenue 等模块拆成可以单独安装的包(「而每个包可以独立安装」),今天做不到:安装时没有任何地方检查依赖是否已装,单独发布的模块还会因为共用
crm命名空间被安装门拒绝。这份 ADR 定下契约,运行时卡在它合并后再切。风险与代价(含回滚)
只改文档,不动代码和 schema。业务上的代价写进了 ADR:卸载或升级被依赖的应用暂时不拦(推迟项),管理员可能把依赖方晾在那里;单独发布的模块和主应用各有版本号,ADR-0130 在同一发布物内消除的版本矩阵会在跨发布物时回来,只靠声明的版本范围约束。合并后的运行时卡按 S 估(裁决原估检查本身为 XS–S),另有作者侧规则和快照转换两张小卡。回滚:撤销本 PR 的合并即可,没有任何运行时状态。
席位意见
你要做的
读 ADR 里「Open questions for the maintainer's merge decision」十二条(第一至四条与第十一、十二条定机制:同一发布物内送达的依赖算已装且只看在不在;只在三个安装动作时检查,开机重注册和安装后的 PATCH 改动都不检查;命名空间共有关系由依赖方单向声明,门禁不论哪一方后注册都按这一条声明判定,否则装了模块之后 HotCRM 自己就再也升不了级;只认直接声明,外加「同一被依赖方下的模块互为同门」这一条,否则第二个模块就不能独立安装;安装后 PATCH 改
dependencies暂不判定,这道门只到得了手工编写的包,且整体改写可以删掉作为共有凭据的那一条;开机重注册的两条路径各自用同一个排序器按manifest.dependencies排序后再注册,而不是让同门规则在父包未注册时放行。五至八条定拒绝的形状:422(同一道门上也有 409 的先例,取 422 是与注册表门禁一族一致);版本或范围解析不了即拒绝,与协议握手的「警告并放行」相反,并要求运行时卡写明支持的文法,树里三个读取器没有一个兼有完整文法与精确到补丁号的粒度,扩展握手那一族;每条声明都是必需、optional投影为 false;只判本包自己的声明。第九条:D8 的记录到数组转换另立一张卡,先读控制面怎么解析快照。第十条:对 #14488 裁决的读法,「发布物整体安装」未变,「不需要单独安装一个模块」所否定的需求如今由另一个发布物满足,即该裁决自己点名的拆出路径)。另有一条 HotCRM 侧前提尚未核实:sales 发布物里其余crm包须直接声明 sales,否则模块安装和 HotCRM 升级都会在命名空间门上被拒。同意就合并,合并即接受;有异议就在 PR 里点出哪一条。What changed
docs/adr/0141-inter-app-dependency-declared-checked-before-install.mddependency_missingissue per unmet entry; the three install acts it runs at, the three re-registration paths and the manifestPATCHit does not; fail-closed on an unparseable range, the opposite of the handshake beside it, paired with a stated range grammar — none of the tree's three readers has both the documented grammar and exact-version granularity; the handshake family inmetadata-coreis extended as a sibling function, the handshake's verdicts unchanged — and an author-sideos lint/os validaterule) · D3 the six deferred items, each with its interim state and trigger (thePATCHitem noting the door reaches only authored packages and can delete D5's proof) · D4 permissions unchanged, record-level semantics an open section of three questions (incl. the ADR-0086 D4 own-namespace discriminator that D5 dissolves across artifacts) · D5 dependency-declared namespace co-ownership, one-way, read from either end (the gate refuses any sitting owner outside the co-owner set, so HotCRM could not otherwise be upgraded once a module is installed — and the free upgrade guard that brings), with a sibling arm (two modules under one declared parent that holds the namespace), the precondition that the app's own companion packages declare it, and the requirement that each hydrate path sorts its batch through the one sorter (the seals and the never-recovered refusal otherwise; ledger entries as nodes; a cycle degrades to source order, never throwing out of the hydrate), D3 uniqueness across co-owners and siblings, ADR-0130 D8's deferral now ending at marketplace publishing · D6 what ADR-0028/0019/0130 now say · D7 the first consumer (a HotCRM module shipping alone astype: 'app', revenue first) with three HotCRM-side preconditions, not re-verified · D8 the three dependency shapes; the record-to-array snapshot conversion split off as its own card · a merge-decision list of twelve drafter choices (1–4, 11 and 12 the mechanism, 5–8 the refusal's shape, 9 the D8 conversion as its own card, 10 the reading of ruling5645322259)docs/adr/0028-metadata-naming-and-namespace-isolation.mddocs/adr/0019-app-as-consumer-unit.mdRelatedheader line; text unchanged (ADR-0130 D2's recording convention)docs/adr/0130-release-artifact-as-co-ownership-boundary.mdnot_planned, ruling5645322259). No original text edited. The addendum names the sibling arm beside the one-way relation, and quotes, byte-exact, ruling5645322259's premise sentence, reading it in two halves: an artifact installs whole (unchanged, D2 stands); the need its second half denied is now met by another artifact, the carve-out path the ruling itself named; its disposition standsMeasurements (at
origin/main47af3dde42)manifest.dependencies" — holds for checking, fails for ordering by one reader:resolveArtifactPackageEntries(packages/core/src/artifact-packages.ts) feeds every declared id toresolvePluginOrderas an optional edge and skips an id outside the artifact silently. No install door checks presence or version;dependency_missingoccurs only inapp-install.zod.ts, its test andcontent/docs/references/system/app-install.mdx— the only three tracked occurrences (zero producers). Recorded in the ADR's Context and Verification notes; no decision moves.NamespaceConflictErrorinpackages/objectql/src/registry.ts" — holds. Gate and throw areSchemaRegistry.installPackage/NamespaceConflictError; the co-owner set isArtifactInstallScope.packageIds(artifact scope only);refuseCoOwnedObjectNameCollisionreads the same set and skips non-co-owners by design, which is why D5's one added source makes both halves of the ADR-0130 D3 pair follow. The line numbers the HotCRM input and the ruling cite have moved; the ADR cites symbols.PackageDependencySchemaarray, and a third —AppManifestSchema.dependencies, bare ids with no range, in the same file asdependency_missing, no reader outsidepackages/spec.os package publishsnapshots the record shape intomanifest_json, whose declared schema is the array; the control-plane side lives inobjectstack-ai/cloudand is not measured.0136and0140belong to proposals that never landed (PR docs(adr): ADR-0136 declared journeys as the priority anchor #18480 closed 2026-09-18, superseded by the North Star; PR docs(adr): ADR-0140 — v18 is the first LTS line,latestfollows it, breaking changes batch into quarterly majors #25618 withdrawn 2026-10-24 by the maintainer's ruling in session); no landed record or open PR carries either number. This record is0141.examples/app-multi-package(orders → core, same artifact); each package declares its ownversion: '1.0.0', andcomposeStacks(…, { manifest: 'preserve' })keeps each manifest as authored — why D2 counts a co-delivered dependency as present and judges it for presence only.rangeAdmitsMajor/alternativeAdmitsMajor(packages/metadata-core/src/protocol-handshake.ts) read*/x/latest,^,~,N.x,||, space- or comma-joined intersections, hyphen ranges and bare versions, but at major granularity (^6.2.0admits 6.1.0);parseRangeFloor(same school, by its docstring) computes a floor only;SemanticVersionManager(packages/core/src/dependency-resolver.ts) compares full versions over a narrow grammar (6.x, intersections and||answer false or throw;^6throws inparse) and has no caller inpackages/outside its own file and test. No package underpackages/imports a semver library (rootsemveris a devDependency).PATCH /api/v1/packages/:id(packages/runtime/src/domains/packages.ts) first runsrequireWritablePackage(…, 'edit')—422 WRITABLE_PACKAGE_REQUIREDfor a package provided by code or an installed app (ADR-0070 D2) — then rewritesdependencieswhole by reference toManifestSchema.shape.dependencies, with no in-use check; it can delete an entry. The post-install edit D3 defers (merge question 11).sys_packageshydrate (packages/services/service-package/src/index.ts) iteratespackageService.list()and, on aninstallPackagethrow, logs the package "was NOT hydrated into the registry" and leaves it absent for the process; the install-localkernel:readyrehydrate registers ledger entries inreaddirSyncorder (local-manifest-source.ts#list).InstalledPackageSchemarecords no artifact; the manifest service's scope is not persisted. Hence D5's companion precondition and the per-path sort (merge question 12).resolvePluginOrderthrows on a cycle through either edge kind andresolveArtifactPackageEntrieskeeps that throw for one artifact; D5 states the hydrate sort does not inherit it (cyclic members fall back to source order with a logged refusal).Gates
Derived with
node scripts/pm/dispatch-gates.mjs --commands(change set vs merge base47af3dde4, 4 files, +851 / −16), all 20 run at head02456273(re-derived on that head), every oneexit 0:check-adr-links(+--self-test),check-adr-symbol-anchors(+--self-test; 2473 anchors across 143 records resolve),check-ci-filter-parity,check-closing-keyword-parity(+--self-test),check-comment-mask-corpus,@objectstack/lint check:doc-formula-expressions,check:adr-anchors,check:closed-ceremony-text,check:cross-package-test-inputs,check:doc-authoring,check:driver-memory-census,check:gitlink-declared,check:nul-bytes,check:pm-governed-merges,check:pm-prior-rulings,check:refd-timer-probe,check:watch-hint-literal.dispatch-gates --ran: 20 derived, 20 run, 0 NOT-MEASURED, 0 UNRUN (a derived zero — every line carried its exit code).The record was re-drafted at
247fe2faatCONTRACT_REVIEW_TIERwith effort xhigh, on the owner's order that ADR text runs at that tier (card #25729). Contract review PASS at247fe2fa(comment6392433266); its five nits landed infa733808. The independent review's eight accepted items (「全部照改」) landed in2bf30f03. The independent full review of2bf30f03(PASS WITH FIXES, F1–F11) landed in5c4071ef. The delta review of5c4071ef(PASS WITH FIXES, N1–N4) landed in02456273. The final delta check of02456273is PASS (record on this PR).No changeset:
docs/adr/**publishes nothing (skip-changeset).Acceptance notes
docs/architecture/module-split-plan.md, hotcrm#2270 and hotcrm#2271 are cited as the rulings cite them; that repository is not readable from this session.6369704387says "revenuefirst per ADR-0130 §1.3(c)"; §1.3(c) names CPQ as the unsellable module. The ADR records revenue-first as HotCRM's own ordering; nothing moves.resolvePluginOrder(merge question 12) — ledger entries as nodes in the install-local path, a cycle degrading to source order rather than throwing out of the hydrate —; cross-path order and thekernel:readyseal/hydrate hook order are Open item 2.optional→ false; own entries only; the D8 conversion as its own card; the two-halves reading of ruling5645322259; the post-installPATCHdeferred (authored packages only); each hydrate path sorting bymanifest.dependencies.Generated by Claude Code