From c2b0e3ad1b7bc4170352cda80eb5953676715c63 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 31 Oct 2026 15:19:03 +0000 Subject: [PATCH] =?UTF-8?q?docs(adr):=20ADR-0070=20D2=20amended=20?= =?UTF-8?q?=E2=80=94=20an=20admin=20may=20disable=20a=20type:=20module=20s?= =?UTF-8?q?ub-package=20of=20an=20installed=20app?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Records the maintainer's ruling of 2026-10-30 (「大包 + 设置里启用模块」, then 「同意」) as a dated amendment note under D2, naming the tie the disable door reads as shipped: a type: module package whose manifest.dependencies directly names an installed type: app package, not system/cloud scoped. Authoring stays refused (isWritablePackage unchanged), the navigation fold drops a disabled package's contributions, and its record-change flows are withheld for in-process writes. The in-process access question is recorded as open. The Status line carries the dated pointer the registry's convention requires. Claude-Session: https://claude.ai/code/session_01FEkQ5nCt4UPJjChW4GyQxQ Co-authored-by: Claude --- docs/adr/0070-package-first-authoring.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/adr/0070-package-first-authoring.md b/docs/adr/0070-package-first-authoring.md index a41aad0b0d2..572cd206aed 100644 --- a/docs/adr/0070-package-first-authoring.md +++ b/docs/adr/0070-package-first-authoring.md @@ -1,6 +1,6 @@ # ADR-0070: Package-first authoring — every runtime-authored item lives in a writable package (base); no orphans -**Status**: Accepted (2026-06-24) — P1–P3 implemented, merged & live-verified (see *Implementation status*); D4–D6 remaining · **Superseded in part by [ADR-0146](./0146-studio-designs-setup-administers-one-editor-per-item-paired-customize-package.md)** (2026-10-31) — D3's prompt to create a base, for the paired case only: a customer's first customization of an installed base lands in the auto-created `.custom` package without a prompt (ADR-0146 D3); D5's destination for a package-less overlay row of a packaged item: the base's `.custom`, as a declared override, after which the package-less write closes (ADR-0146 D11). D1, D2, D4 and D6 stand; D3's prompt stands everywhere else. +**Status**: Accepted (2026-06-24) — P1–P3 implemented, merged & live-verified (see *Implementation status*); D4–D6 remaining · **Superseded in part by [ADR-0146](./0146-studio-designs-setup-administers-one-editor-per-item-paired-customize-package.md)** (2026-10-31) — D3's prompt to create a base, for the paired case only: a customer's first customization of an installed base lands in the auto-created `.custom` package without a prompt (ADR-0146 D3); D5's destination for a package-less overlay row of a packaged item: the base's `.custom`, as a declared override, after which the package-less write closes (ADR-0146 D11). D1, D2, D4 and D6 stand; D3's prompt stands everywhere else. · **Amended** (2026-10-31, [#28117](https://github.com/objectstack-ai/objectstack/issues/28117) — the maintainer's ruling of 2026-10-30, 「大包 + 设置里启用模块」, then 「同意」: a `type: module` sub-package of an installed app may be **disabled**, not authored into, by an admin, on the tie PR [#28308](https://github.com/objectstack-ai/objectstack/pull/28308) shipped; see the amendment note under D2. D2's two kinds and its writability predicate stand.) **Deciders**: ObjectStack Protocol Architects **Builds on**: [ADR-0005](./0005-metadata-customization-overlay.md) (`allowOrgOverride` / runtime overlay model; one Zod source per type), [ADR-0048](./0048-cross-package-metadata-collision.md) (one app per package; package id is the addressing unit), [ADR-0033](./0033-ai-assisted-metadata-authoring.md) (AI authors metadata as drafts), [ADR-0067](./0067-commit-history-and-rollback-for-ai-authoring.md) (commit history & rollback for AI authoring) **Consumers**: `@objectstack/objectql` (`protocol.saveMetaItem` write path), `@objectstack/spec` (metadata-type registry), `../objectui` (Studio create flow, package scope selector, `PackagesPage`), `../cloud` (`service-ai-studio` — `create_metadata` / `resolvePackageId` / `apply_blueprint`) @@ -63,6 +63,8 @@ A runtime **create** (`intent = runtime-only`, i.e. not an overlay of an artifac The kernel exposes a single predicate — *is this package writable?* — reused by all surfaces (cloud's `resolvePackageId:319` read-only check is the seed of this). +> **Amended 2026-10-31 ([#28117](https://github.com/objectstack-ai/objectstack/issues/28117), the maintainer's ruling of 2026-10-30; shipped in PR [#28308](https://github.com/objectstack-ai/objectstack/pull/28308), `7abe151b`) — a `type: module` sub-package of an installed app may be disabled (not authored into) by an admin.** The maintainer, verbatim: 「大包 + 设置里启用模块」, then 「同意」 — a suite ships as one artifact, an `app` package with `module` packages beside it; the customer's own admin switches its modules off and on, per environment; and "disabled" has exactly one meaning: hidden, refused on the data plane, data kept. *Read-only* above means *may not be authored into*; it does not mean *may not be switched off*. So `PATCH /api/v1/packages/:id/disable` reads one exemption before the writability check, `packages/runtime/src/domains/packages.ts#isDisableableAppModule`, and the tie it reads is the one both install paths leave on the registry row — a code boot and a marketplace install alike, since neither keeps the artifact's own package list past registration: the row's `manifest.type` is `'module'`, its `manifest.dependencies` directly names a package installed here whose `manifest.type` is `'app'` (ADR-0141 D1), and its own `manifest.scope` is not `system` / `cloud`. Everything else answers the door as before. The predicate above does not move: `packages/metadata-protocol/src/package-writability.ts#isWritablePackage` is unchanged, `writable` keeps meaning "may author into" and stays `false` for a module, so edit, publish, revert, delete and overwrite still answer `422 WRITABLE_PACKAGE_REQUIRED`; the door's own verdict is served beside it as `disableable` (`packages/spec/src/api/package-api-assembled.zod.ts#SERVED_ROW_VERDICTS`), computed by the predicate the door asks, so no client re-derives the rule. While the module is disabled, its objects answer `404 OBJECT_PACKAGE_DISABLED` on the data plane and the by-name reads, as any disabled package's do; the navigation fold drops its contributions (`packages/objectql/src/registry.ts#applyNavContributions`); and its record-change flows are withheld for in-process writes too (`packages/triggers/trigger-record-change/src/record-change-trigger.ts#RecordChangeTrigger`). Enable restores all three; the data was never touched. Not decided here: whether an app's own in-process access to a disabled module's objects (a hook's `ctx.api`) should meet the data plane's refusal — today it is served, pinned and documented, and the question is with the maintainer as decision letter `6538936519` on #28117. + ### D3 — Create-flow contract: select-or-create a base first, enforced everywhere [new] Every authoring surface follows the same contract; none silently defaults into a catch-all: