Repository navigation
Commit 8e6acf4
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1X1MLz3h8qcUsNrPdNUHERHj)_
domain:server#2 · os-project-manager · session_01FEkQ5nCt4UPJjChW4GyQxQ
Fixes #28285
Clause-②: no — governed prose only; no accept set, refusal or served
shape moves here. The behaviour the note records shipped in PR #28308
(`7abe151b`), which declared its own `yes (widening)`.
Metadata-protocol: none
## 维护者速读
- **改了什么**:在 ADR-0070 的 D2 下加一条带日期的修订说明,并在 Status 行加同日期的指针:已安装应用下的
`type: module` 子包可以被管理员停用(不是写入)。说明按代码实际行为写:停用门读取的归属判据(模块包的
`manifest.dependencies` 直接声明一个已安装的 `type: app` 包,自身 scope 不是
system/cloud)、写入仍拒绝(`isWritablePackage`
不变)、导航折叠丢弃已停用包的贡献、其记录触发流程在进程内写入时也不派发。
- **为什么改**:#28117 第 6 项,裁决「大包 + 设置里启用模块」/「同意」(2026-10-30);代码已随 PR #28308
合入 main,决策记录要跟上代码。
- **风险与回滚**:零运行时风险,只改一份决策记录;回滚即 revert。ADR-0146 覆盖的是 D3/D5,D2
不在其列,本修订与之不冲突。
- **先看**:`docs/adr/0070-package-first-authoring.md` 的 D2
修订块(一个引用段);Status 行末尾的指针。
- **两分钟验证**:`pnpm check:adr-symbol-anchors`,预期全部锚点解析(本次新增 5 个符号锚);再把 D2
修订块与 `packages/runtime/src/domains/packages.ts` 里
`isDisableableAppModule` 的注释对照。
- **需要你做的**:Tier H(`docs/adr/**`):你的 approve 或亲手合并。另有一个未决问题不在本 PR
内:应用自己的 hook 在进程内读写已停用模块的对象要不要拒绝(决策函 6538936519),修订块只把它记为未决。
## What changes
One file, `docs/adr/0070-package-first-authoring.md`:
1. **An amendment note under D2**, in the registry's dated `> **Amended
…**` form (the form ADR-0087 and ADR-0005 use), recording the
maintainer's ruling of 2026-10-30 verbatim and untranslated (「大包 +
设置里启用模块」, then 「同意」) and what shipped in PR #28308 (`7abe151b`):
- *read-only* in D2 means *may not be authored into*, not *may not be
switched off*;
- the tie the disable door reads, as `isDisableableAppModule` measured
it on both install paths: `manifest.type` `'module'`,
`manifest.dependencies` directly naming an installed `type: 'app'`
package, own `manifest.scope` not `system` / `cloud`;
- what does not move: `isWritablePackage` unchanged, `writable` still
means "may author into" and stays `false` for a module, edit / publish /
revert / delete / overwrite still answer `422
WRITABLE_PACKAGE_REQUIRED`; the door's verdict is served beside it as
`disableable` (#28318, on `main`), computed by the same predicate;
- what disabled means for the module: `404 OBJECT_PACKAGE_DISABLED` on
the data plane and the by-name reads, its nav contributions dropped by
the fold, its record-change flows withheld for in-process writes, enable
restores all three, data kept;
- the in-process question (an app's own hook reading a disabled module's
objects through `ctx.api`) is recorded as **not decided here**, with the
maintainer as decision letter `6538936519` on #28117.
2. **A dated pointer on the Status line**, the form ADR-0146's
"Status-line pointers" section fixes for an amended record (`·
**Amended** (date, …) — clause; see the amendment note under D2`).
Nothing else on the Status line moves; D3–D6 are untouched.
**ADR-0146 check.** Its supersession of ADR-0070 covers D3's prompt and
D5's destination for the paired case, and its pointer on ADR-0070's
Status line says "D1, D2, D4 and D6 stand". D2 is not the superseded
part, so the amendment stands beside ADR-0146 and this PR touches no
other record.
Five symbol anchors are new, all resolving (`check:adr-symbol-anchors`
below):
`packages/runtime/src/domains/packages.ts#isDisableableAppModule`,
`packages/metadata-protocol/src/package-writability.ts#isWritablePackage`,
`packages/spec/src/api/package-api-assembled.zod.ts#SERVED_ROW_VERDICTS`,
`packages/objectql/src/registry.ts#applyNavContributions`,
`packages/triggers/trigger-record-change/src/record-change-trigger.ts#RecordChangeTrigger`.
## Tests
None: a decision record, no tested behaviour changes, so no ablation.
The gates that read `docs/adr/**` are the verification.
## Acceptance notes
- **Governed surface.** `node scripts/pm/check-governed-merges.mjs
--test docs/adr/0070-package-first-authoring.md` answers exit 3: "⛔
GOVERNED — a human merge is the review record for this PR (#9495 regime)
… ⚖️ landing tier: H(人合) — the maintainer's hand, or an authorized
APPROVED review (GOVERNED_APPROVERS) and then the owning seat lands it.
docs/adr/** ×1". This PR stays draft; no seat readies, queues or arms
it.
- **Model tier.** `dispatch-gates.mjs --tier` on the path: "Model tier —
MANDATORY: claude-fable-5-1 … the same order fixes the effort floor at
xhigh". This session ran at `claude-fable-5-1`, effort `xhigh` (the
session record).
- **`skip-changeset`** rides the create: `docs/adr/**` publishes nothing
(fast track).
- **The generated ADR index** `docs/adr/README.md` is current after the
edit (`node scripts/adr-index.mjs --check`: "OK — docs/adr/README.md is
current and every record parsed"), so it is not touched.
- **Not here, by the order:** the in-process access decision (letter
`6538936519`), the Console half (#28118), D4–D6.
- **Lint.** `pnpm exec eslint --no-inline-config
docs/adr/0070-package-first-authoring.md` exits 0 with "File ignored
because no matching configuration was supplied": Markdown is outside the
eslint config, and the doc gates below are the style authority for this
path.
- **The PR body's first two lines** are the harness attribution block
the project requires; the seat signature follows it.
## Verification
Gates from `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` on this diff (21 commands), each run on head
`c2b0e3ad` with its exit code captured before any pipe:
| # | Gate | Exit |
|:--|:--|:--|
| 1 | `node scripts/check-adr-links.mjs` | 0 |
| 2 | `node scripts/check-adr-links.mjs --self-test` | 0 |
| 3 | `node scripts/check-closing-keyword-parity.mjs` | 0 |
| 4 | `node scripts/check-closing-keyword-parity.mjs --self-test` | 0 |
| 5 | `node scripts/check-comment-mask-corpus.mjs` | 0 |
| 6 | `node scripts/ci/plan-test-core-shards.mjs --self-test` | 0 |
| 7 | `pnpm --filter @objectstack/lint run
check:doc-formula-expressions` | 0 |
| 8 | `pnpm check:adr-anchors` | 0 |
| 9 | `pnpm check:adr-index` | 0 |
| 10 | `pnpm check:adr-symbol-anchors` | 0 |
| 11 | `pnpm check:ci-filter-parity` | 0 |
| 12 | `pnpm check:closed-ceremony-text` | 0 |
| 13 | `pnpm check:cross-package-test-inputs` | 0 |
| 14 | `pnpm check:doc-authoring` | 0 |
| 15 | `pnpm check:driver-memory-census` | 0 |
| 16 | `pnpm check:gitlink-declared` | 0 |
| 17 | `pnpm check:nul-bytes` | 0 |
| 18 | `pnpm check:pm-governed-merges` | 0 |
| 19 | `pnpm check:pm-prior-rulings` | 0 |
| 20 | `pnpm check:refd-timer-probe` | 0 |
| 21 | `pnpm check:watch-hint-literal` | 0 |
`dispatch-gates --ran`: "21 derived famil(ies) accounted for — 21 run, 0
NOT-MEASURED (a DERIVED zero — all 21 recorded an exit code and none of
them is 3)". The first pass recorded exit 3 (PREREQUISITE NOT MET:
`@objectstack/formula` and `@objectstack/lint` not built) on
`check:doc-formula-expressions`; after `pnpm exec turbo run build
--filter=@objectstack/formula --filter=@objectstack/lint` under the
verify lock (VERDICT command-exit 0, 1m36s) the gate ran and exited 0,
and the record above carries that run.
<details><summary>Readings quoted from the gate logs</summary>
- `node scripts/check-adr-links.mjs` → ✅ check-adr-links: 962 relative
link destination(s) under docs/adr/ resolve
- `node scripts/check-adr-links.mjs --self-test` → ✅ check-adr-links
--self-test: discrimination, census, ADR-0046 pin and baseline staleness
all verified
- `node scripts/check-closing-keyword-parity.mjs` →
check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and
both measured separators; sweep found 5 file(s) carrying the grammar
across 23186 tracked file(s), all registered).
- `node scripts/check-closing-keyword-parity.mjs --self-test` → ✓
check-closing-keyword-parity --self-test: 40 assertions, 5 mutations of
the shipped parsers each driven to red.
- `node scripts/check-comment-mask-corpus.mjs` → ✓ comment-mask corpus
sweep [scripts/js-comment-mask.mjs]: 19531 files, 0 disagree, 0
unparseable, 186.4s (comparator self-test: 26 cases pass).
- `node scripts/ci/plan-test-core-shards.mjs --self-test` → ✓
plan-test-core-shards self-test: 23 case(s) across 5 batteries.
- `pnpm --filter @objectstack/lint run check:doc-formula-expressions
(rerun after the build)` → ✓ check:doc-formula-expressions (field-level
`*When`, #11407): 15 predicate(s) on a statically determinable field
layer judged clean; 12 skipped as undeterminable.
- `pnpm check:adr-anchors` → check-adr-anchors: OK (95 anchored file(s),
every governing ADR still referenced; 142 decision number(s), each
naming one decision or an allowlisted pair; 62056 citation(s) across
8887 file(s) resolve, 37 of them stem-qualified to one record; 2025
decision-let
- `pnpm check:adr-index` → adr-index --report: OK — docs/adr/README.md
is current, every record parsed, self-test 46 assertions green.
Report-only: this line never reds the step.
- `pnpm check:adr-symbol-anchors` → ✅ check-adr-symbol-anchors: 2952
anchors across 148 records resolve — 691 symbol (663 declaration, 28
literal), 2214 file-level, 47 cross-repo, 6 exempt, 31 continuation. 0
line anchors survive in the path-anchored, backticked continuation,
fenced comment head
- `pnpm check:ci-filter-parity` → OK: all 8 root input(s) of `test` /
`test:repo` in turbo.json are covered by `core` or `crosspkg`, which the
`test` job's `if:` reads.
- `pnpm check:closed-ceremony-text` → ✓ check-closed-ceremony-text: 5537
shipped file(s) read; none says the ownership ceremony is closed.
- `pnpm check:cross-package-test-inputs` → OK: 31 package(s) read
outside themselves, all declared, and turbo.json hashes every declared
glob (6 of them on a split "test:repo" task); 13 walked root(s) judged,
3 on ACCEPTED_WALK_RADII; 4061 test file(s) import a workspace sibling
by bare specifier over
- `pnpm check:doc-authoring` → ✓ doc authoring guard: sibling-package
prose ids hold the baseline — 0 pinned site(s) across 0 file(s), 116712
string(s) read in 1627 parsed source(s) (seen floor 40000 strings / 600
sources), no growth, no burn-down unrecorded.
- `pnpm check:driver-memory-census` → check-driver-memory-census: OK —
every declaration is ledgered, every ledger entry is live, and every
ruled file states "#6664 census: 2 ruled consumers". This gate polices
the census, never investment.
- `pnpm check:gitlink-declared` → check-gitlink-declared: OK (23186
index entries -- 0 gitlink(s) at mode 160000; no .gitmodules in the
index, so nothing is declared; nothing to declare).
- `pnpm check:nul-bytes` → check-nul-bytes: OK (scanned 23087 text
file(s) -- 23087 tracked, 0 untracked-not-ignored; skipped 99 binary; no
raw ASCII control bytes).
- `pnpm check:pm-governed-merges` → ✓ check-governed-merges --self-test:
537 assertions (the unified governed predicate + near misses, subject→PR
spellings, window parsing, the #12633 landing window — the QS-7
regression pin in both directions, the topological close beyond the
budget, the unprov
- `pnpm check:pm-prior-rulings` → ✓ check-prior-rulings self-test: 155
cases pass (term derivation with its length floor, stopwords and
code-span splitting; the governing-text carrier in both measured shapes
plus the absent case that is 23 of 27 on the live board; the
backticked-CLAUSE and lea
- `pnpm check:refd-timer-probe` → OK check-refd-timer-probe: 19525
source file(s) swept; the process-global timer probe is read in
packages/qa/refd-timer-testkit/src/index.ts and nowhere else.
- `pnpm check:watch-hint-literal` → ✓ check-watch-hint-literal: 75
declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 49,
ROOT_FILE_WATCH_HINTS 14, ROOT_WATCH_HINTS 3, DECLARED_WATCH_HINTS 9 --
every one an array of quoted literals inside its own statement, all 188
of those literal
</details>
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(`packages/spec` untouched.)
- [x] 2. Every name I added uses the casing for its kind. (No names
added.)
- [x] 3. No formatter ran; `pnpm lint` scoped to the file is ignored by
config (exit 0, see Acceptance notes).
- [x] 4. One issue, one branch off an up-to-date `main` (`5da3a8ce`),
one pull request.
- [x] 5. A change that publishes carries a changeset: nothing publishes
(`skip-changeset`); both declaration lines are filled in above.
- [x] 6. `packages/spec` touched: n/a.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English (the 维护者速读 block is the
required Chinese summary; the ruling is quoted verbatim).
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FEkQ5nCt4UPJjChW4GyQxQ)_
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 96d12ec commit 8e6acf4
1 file changed
Lines changed: 3 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
| 66 | + | |
| 67 | + | |
66 | 68 | | |
67 | 69 | | |
68 | 70 | | |
| |||
0 commit comments