Skip to content

docs(adr): ADR-0146 — Studio designs, Setup administers; one editor per metadata item; paired <base>.customize package - #28045

Merged
os-zhuang merged 4 commits into
mainfrom
claude/project-thread-pm92gt
Oct 30, 2026
Merged

os-zhuang merged 4 commits into
mainfrom
claude/project-thread-pm92gt

Conversation

@os-bill

@os-bill os-bill commented Oct 30, 2026

Copy link
Copy Markdown
Collaborator

Requested by Bill · project thread

Part of #27356 (rows 12, 14, 19 — the decision record behind their rulings). Fixes nothing.

维护者速读

  • 改了什么:新增 ADR-0146(Proposed)+ 重新生成 docs/adr/README.md 索引。只有文档,不改代码、不改 schema、不带 changeset。
  • 为什么改:你在决策线程 2026-10-30 10:04–10:28Z 的裁定——Studio 管全部元数据(设计 + 目录 + 源码 / 分层 / 历史 / 诊断),Setup 只做管理;每一项只有一个编辑处;只读包上就地做覆盖;开关与克隆放在 Studio 的流上;只读包也画「在 Studio 中查看」;每个基包配一个真实的 <base>.customize 包,Studio 里当一个界面看。
  • 风险与回滚:ADR 合并前不生效;合并后也只是声明,实施按 Rollout 四期切卡。回滚 = 改 Status 行。
  • 先看:D1–D5(边界、一个编辑处、配对包、开关在项上、入口),D6(明确不做:字段表 C),D7(搬家前 Setup 要补的三张管理页),Open questions 三条等你定。
  • 两分钟验证:node scripts/adr-index.mjs --check 与 node scripts/check-adr-links.mjs 都通过(本地已跑)。
  • 需要你做的:审 D1–D7 与三条 Open questions;同意则 hand-merge(Prime Directive feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14)。合并后我按 Rollout 切卡。

What changes

  • docs/adr/0146-studio-designs-setup-administers-one-editor-per-item-paired-customize-package.md — new Proposed record. D1 Studio owns all metadata, Setup owns administration; D2 one editor per item, in-place overlay editing on a read-only package; D3 a paired real <base>.customize package shown as one surface with its base; D4 the ADR-0126 §7 toggle and clone offered on the item in Studio, Setup keeps the overview; D5 the app → Studio bridge draws a browse entry for read-only packages (objectui#12615's verb changes, the entry stays); D6 not done (no fields table — row 14 letter C; no Setup designer; no global customization scope); D7 the Setup administration pages the move owes. Rollout in four phases; three open questions for the maintainer.
  • docs/adr/README.md — regenerated by scripts/adr-index.mjs (one new row).

Why

HotCRM's 18.2.0 browser pass (#27356 rows 12, 14, 19) found two designers per flow or view with two save models, no Studio entry for a code package's app, and delete / reset / toggle / clone reachable only in Setup. The maintainer ruled the boundary in the project decision thread rather than row by row; this record writes that ruling down once so the rows and every later type are executed by rule. The maintainer's words are quoted verbatim at each decision.

Verification

  • node scripts/adr-index.mjs --check → OK (index current, every record parsed).
  • node scripts/check-adr-links.mjs → 942 relative link destinations resolve.
  • No code, no tests, no changeset (docs-only, unpublished surface).

Clause-②: no
Metadata-protocol: none

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (n/a — docs only)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes. (ADR checks run; no code touched)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. (nothing publishes)
  • 6. packages/spec touched: its generated artifacts are regenerated and committed. (not touched)
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English. (the 速读 block is the project's maintainer-facing convention; the record itself is English with the maintainer's verbatim quotes)
  • The Before Submitting list in CONTRIBUTING.md is done.

Generated by Claude Code

…er item; paired customize package

Proposed record drafted on the maintainer's instruction in the project
decision thread for #27356 rows 12 / 14 / 19. Declares the boundary
between Studio (all metadata: design, catalog, source, layers, history,
diagnostics) and Setup (administration of the deployment), one editor
per metadata item, in-place overlay editing on a read-only package, the
operational switches on the item, a browse entry for read-only packages,
and a paired `<base>.customize` package shown as one surface with its
base. Implements nothing; the rollout is cut as cards after the merge.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CaRN6wdrRyLKJSCMYH
@objectstack-fleet

Copy link
Copy Markdown
Contributor

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract-review-claim: session_01L6bhgV4fMGJHSdSdx3u9mK · head 46a2f1bc88c85aaaaf7a20c5dd2375f180b34d47 · 2026-10-30T12:03Z


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 46a2f1bc88c85aaaaf7a20c5dd2375f180b34d47
Local-runs: none
Scope: docs/adr/0146-studio-designs-setup-administers-one-editor-per-item-paired-customize-package.md, docs/adr/README.md + packages/spec/src/kernel/metadata-plugin.zod.ts, packages/spec/src/kernel/manifest.zod.ts, ui/packages/app-shell/src/hooks/useStudioDesignGate.ts, ui/packages/app-shell/src/hooks/useCanAuthorMetadata.ts, ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx, ui/packages/app-shell/src/views/studio-design/packages-io.ts, ui/packages/app-shell/src/views/studio-design/datasetCreate.tsx, ui/packages/app-shell/src/views/metadata-admin/DirectoryPage.tsx, ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx, ui/packages/app-shell/src/views/setup/PackagedAutomationPage.tsx, packages/runtime/src/flow-clone.ts, packages/runtime/src/domains/activation-gate.ts, packages/runtime/src/domains/automation.ts, packages/runtime/src/domains/packages.ts, packages/rest/src/rest-route-ledger.ts, docs/qa/platform-checklist/areas/studio-authoring.json, docs/qa/platform-checklist/areas/ux-conventions.json, ui/e2e/live/studio-object-designer.spec.ts, docs/adr/0005-metadata-customization-overlay.md, docs/adr/0010-metadata-protection-model.md, docs/adr/0029-kernel-object-ownership-and-platform-objects-decomposition.md, docs/adr/0070-package-first-authoring.md, docs/adr/0126-packaged-metadata-customization-model.md, docs/adr/0131-total-organization-ownership-no-null-organization-id.md, docs/adr/0141-inter-app-dependency-declared-checked-before-install.md, docs/adr/0142-ownership-queue-membership-claim-and-assignment-rules.md, scripts/adr-index.mjs

Inputs: card #27356 (body and all 24 comments, ruling record 6520616687 included), PR #28045 (body, file list, net diff of 2 files, +254/-3), and the 46 check-runs on the head. The hits are the whole diff: there is no lane-seat remainder.

① Derived judgments

  • Accept-set / published surface: none changes — right. Two files under docs/adr/, no code, no schema, no package moves. The labels documentation, size/m, skip-changeset match the file list.
  • Governed surface: docs/adr/** is Tier H — right. The PR is draft, auto_merge null; the landing is the maintainer's hand-merge (Prime Directive feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14), which the PR body and the ADR's status line both say. This record is the seat's contract-tier review of record, not a landing authorization.
  • ADR registry state at the head — right. One file carries 0146; 147 records over 142 numbers under docs/adr/ (the README summary's figures); the filename matches ADR_FILENAME; the status line starts **Status**: and statusKeywordOf reads Proposed (first keyword inside the 80-character window); the header window yields the date 2026-10-30.
  • README regeneration — right, and derived, not trusted: the lint step check:adr-index is report-only by maintainer ruling (--report exits 0 on a stale index), so the green Lint · repo & tooling gates run proves nothing about the index. I rendered the row by the generator's own rules (cell(title, 110), statusCell with its 64-character detail, the date from the status value) and it is byte-identical to line 216 of the committed README; the summary deltas (146 to 147 records, 141 to 142 numbers, Proposed 4 to 5, Total 146 to 147) match the keyword rule applied to the five Proposed records at the head (0050, 0134, 0141, 0142, 0146).
  • Check-runs as gate verdicts — read as follows. The ten required contexts that ran are green: Lint & Repo Gates, TypeScript Type Check, Test Core, Test UI, Governed Surface Queue Guard; Build Core, Lint UI, Type Check UI, Build & E2E UI, Live E2E UI are skipped on a docs-only diff. Check Documentation Links (which runs check-adr-links.mjs) is green, so the record's relative links and section anchors resolve; check:adr-symbol-anchors sits in the green Lint · docs & code guards, so the record's backticked paths resolve at the head. Check Changeset has two runs on this head: failure at 10:33Z (started 10:33:15Z, before the skip-changeset label existed) and skipped at 10:37Z (started 10:37:17Z, after os-bill applied the label at 10:37:06Z). The standing verdict is skipped; the context is advisory either way. PR-body guards green.
  • The "measured on" commit f8633c7 is an ancestor of the head (the main commit of fix(app-shell): permission grid signals cut columns and pins its Object column and field header #27909), so the Context section's readings name a real tree.
  • No model name in the record — right; the only hits for "model" are "customization model" and "save models".

② Semver level

skip-changeset is the right declaration: nothing publishes from any released package, the diff is two Markdown files. Clause-②: no and Metadata-protocol: none are line-initial in the PR body and match the diff (no authorable key, value or type changes; no narrowing). No changeset is owed; none is present. ✓

③ Boundary flags

  • Author: the PR is by os-bill, pushed from claude/project-thread-pm92gt by the review thread (PR footer session session_01Utp6CaRN6wdrRyLKJSCMYH, the same session that posted ruling record 6520616687). It is not under the card's dispatched claim: the claim addendum 6520664956 says "ADR-0146 (draft PR docs(adr): ADR-0146 — Studio designs, Setup administers; one editor per metadata item; paired <base>.customize package #28045) is not part of this claim", and the ruling says the maintainer reviews this PR directly, on 「按照这个方案先更新 issue,写adr。等我审核」. Consistent; PR-body guards green.
  • PR body Human Minimum item 8 (the Chinese 维护者速读 block): answered — it is the card's own dispatch convention (order 6487063370 requires "## 维护者速读 six lines in Chinese"); the record itself is English with verbatim Chinese quotes, as AGENTS.md's Communication rule asks.
  • Quotes verifiable against the card: D5's 「「Edit {app} in Studio」代码包/已安装包一律不画,合理吗?ai写完代码,人类需要进去确认啊」 and D6's 「Studio 的对象设计器,第一栏数据不就是每一个字段吗?表单对应的不也是每一个字段吗」 match ruling record 6520616687 character for character; D4's sentence is the longer form of the fragment the ruling quotes. The D1, D2 and D3 quotes come from the project thread and have no GitHub record; the maintainer's hand-review is their verification. Escalated, see ④ S4.
  • Open question 1 (Setup › Packaged automation after Phase 2: keep as overview or retire): a product call, escalated to the maintainer. The record already says "decided on usage after Phase 2"; I would keep the overview, because it is the one deployment-wide place that lists what is off and why, and D1 assigns operational overviews to Setup.
  • Open question 2 (the pairing name, and constant vs server default): the id hotcrm.customize is valid under the spec's manifest id grammar as the console judges it (isSpecPackageId in packages-io.ts reads ManifestSchema.shape.id), so the suffix is free to choose. Recommendation: one constant in packages/spec (a constant, not logic, Prime Directive ✨ Set up Copilot instructions #2) that the create door and the console both read, so the console never derives the pairing a second way (the rule D5 already follows for writable). Escalated.
  • Open question 3 (a simplified form editor for translation and email-template overlays inside Studio): yes, consistent with "one editor per item" because no pillar edits those types. Flag for the maintainer: it means an administrator changing one line of mail copy enters Studio, which D1's Consequences say an administrator need not do; the record should own that crossing beside the permission-set one. Escalated.
  • Row 12 landed early: PR feat(console): an admin can open a read-only package's app in Studio to look at it #28076 was accepted at 11:59Z (card comment 6522187547), so Context §4 ("the bridge never draws") is true at f8633c7 and false on today's main. D5 and Rollout 2(f) anticipate this; the acceptance edit should note "landed by feat(console): an admin can open a read-only package's app in Studio to look at it #28076" at Context §4.

④ Design judgment

docs/adr/0146-…md against the rulings on the card. D5 = row 12 letter B (eye not pencil, zh 「在 Studio 中查看 {app}」, same gate, no second derivation of writable, kernel and unread lists draw nothing) ✓. D6 = row 14 letter C with the ruling's reasons (objectui#5813, objectui#12450) ✓. Rollout 1 = row 19 letter A1 (Delete flow now, writable packages only, same DELETE /meta/:type/:name door, pending draft discarded first, read-only draws nothing; Reset deferred to Phase 2) ✓. Rollout Phases 2 and 3 enumerate exactly the cards the ruling's "Execution after the ruling" names ✓.

Against the code it describes as current. Registry: 30 entries; allowEnvironmentOverlay: true on exactly view, dashboard, report, translation, email_template; allowRuntimeCreate: false on exactly field, picklist, job, api, capability, sharing_rule, queue, assignment_rule, agent ✓. useStudioDesignGate draws only for a writable package and quotes objectui#12615's sentence verbatim ✓. useCanAuthorMetadata reads manage_metadata; AUTHORING_TRAIL_CAPABILITIES are studio.access and manage_metadata, setup.access dropped ✓. POST /api/v1/automation/:name/toggle, /clone and GET /api/v1/automation/_status exist ✓. flow-clone.ts strips the ADR-0010 envelope ("a clone carries no package"), and the package-less scope is Studio's Automations pillar (STUDIO_ORG_SCOPE_PILLAR) ✓. ResourceEditPage reads the layered envelope (code / overlay / effective), history and audit tabs, diagnostics, and resettable ✓. Four pillars (data, automations, interfaces, access) ✓. The two QA checklist files and studio-object-designer.spec.ts exist and the checklist itself calls the Setup designer "a sibling door onto the same save" ✓. The package create door is POST /api/v1/packages in packages/runtime/src/domains/packages.ts onto installPackage in packages/metadata-protocol ✓.

Against the ADRs it builds on. ADR-0005 (five types) ✓. ADR-0070 D1/D2 (writable package, overlays only on code packages) ✓; D5/D6 (package-less scope retired, no dumping ground) ✓. ADR-0126 §7.4 is "leaning D2 … final call rides this ADR's merge", a surface choice, so moving the switch and clone onto the item is a legitimate new-ADR reversal under Prime Directive #13 ✓; §9's clone-lineage bullet is kept ✓. ADR-0131 D6 (no door edits a managed definition; customization lives beside it) ✓ — D2's draft writes the overlay row, not the packaged item. ADR-0010 envelope ✓.

Must-fix.

  • M1 — the record charters three items ADR-0126 §9 defers, and says it keeps §9. §9's first bullet reads: "The ServiceNow-style overlay layer (badge / customization list / diff-vs-base / revert / upgrade skip-report) — recorded as the mature direction if customer pull for in-place customization appears; deferred again here." D2 adds a "customized in this environment" chip and an in-place Reset on Studio; D3 adds a "customization" chip, an "only customizations" filter and a landing line "n customized, m switched off" — a badge, a customization list and a revert. The record's Refs line calls §9 "the walls it keeps" and D4 says "ADR-0126 §9 stands in full". Both records would then be in force and disagree on whether the customization list is chartered; a seat cutting Phase 2(a)/(e) cannot tell which wins, which is the failure Prime Directive [WIP] Add Chinese version of the documentation #13 exists to prevent. The lift itself is legitimate — HotCRM's pass and 「或者直接在只读包上做叠加的覆盖管理」 are the "customer pull for in-place customization" §9 named as its own condition, and the chip and count read facts the layered read and the ledger already serve. Fix: a paragraph (in D2 or D6) that names the §9 first-bullet lift — badge, list and revert, on existing facts, no computed drift — keeps diff-vs-base and the upgrade skip-report deferred, strikes "stands in full" and "the walls it keeps", and at acceptance adds the pointer on ADR-0126 §9.

Should-fix.

  • S1 — ADR-0070 D3 is amended, not "answered". D3 says "none silently defaults", "prompt to create a base", "enforced in code"; D3 of this record creates hotcrm.customize "without a package prompt". The reading is defensible (a deterministic per-base target is not the catch-all D3 forbade; ADR-0070's open question 1 left a one-click auto-provision open) but the record should say it amends D3 for the paired case, so the two records do not read as disagreeing.
  • S2 — ADR-0029 D7 is misattributed. D7 defines navigation contributions merged into the setup shell's slots by group and priority; it says nothing about an item "shown greyed with its source". Cite D7 for "contributed items" and own the greyed-with-source presentation as this record's decision (row 20, Studio Interfaces lists only what an app's navigation names: views and record pages off the menu, and a module package's dashboards and pages, cannot be opened in Studio at all #26889).
  • S3 — Context readings to correct (the header says "code read, not run"; these are off). (a) "lists all 30 registered types": DirectoryPage hides field (HIDDEN_TYPES) and shows only the types the active package contributes, fail-closed. (b) "7 metadata types": Studio also creates dataset through its report and widget pickers (datasetCreate.tsx, Studio cannot report or chart on the package's own objects: the report and dashboard-widget dataset pickers list only pre-existing sys_/showcase_ datasets, and Studio offers no way to create a dataset #27518); Phase 4's "dataset" line should say it has a create dialog and no designer. (c) "whose header says 'Automation AUTHORING lives in Studio…'": that sentence is the file's doc comment; the rendered subtitle reads "Flows and actions shipped by installed packages. Turn one off for this deployment, or clone a flow under a new name to customize it. Editing happens in Studio." (d) Context §7 and D7, queue membership: no Setup page or section edits queue membership at the head (only an i18n label queue: '队列'); ADR-0142 D2 (Proposed) defines membership through the existing recipient kinds. Say the queue page is owed by ADR-0142's landing, and that position holders (PositionHoldersSection) and permission-set assignment (AssignedUsersSection) are the two live sections the move re-homes.
  • S4 — provenance that a repository reader can reach. The two /mnt/project-files/decisions/… paths resolve for nobody reading the repository. Cite ruling record 6520616687 on Setup and Studio: suggestions from a full HotCRM browser pass on 18.2.0: user onboarding, packaged automation state, run-now for scheduled flows, admin lists, and Studio entry, field table, flow inspector wording and one designer per item #27356 (posted 10:34Z, two minutes after this head's commit) for the three row rulings, and state plainly that the D1–D3 boundary quotes have no GitHub record and are verified by the maintainer's hand-merge. The bracketed [界面] inside D3's verbatim quote is either an editorial insertion to be marked as one or a rewrite to undo (AGENTS.md: 改写引文就是改写裁决).
  • S5 — D4 names no authority for the Studio ledger switch. activation-gate.ts admits manage_metadata and, under group / isolated postures, only the PLATFORM_ADMIN posture rung (ADR-0126 §5, ADR-0068 D2); a tenant admin in a walled posture gets a 403 naming the platform operator. D4 should say the switch draws disabled under a walled posture for a non-operator, or shows that refusal verbatim as Clone shows its notice — otherwise Studio draws a control that always answers 403, the shape useCanReadTrail already avoids.
  • S6 — cite ADR-0141. D3's "declares a dependency on the base" is manifest.dependencies (packages/spec/src/kernel/manifest.zod.ts), whose pre-install check and shared-namespace co-ownership ADR-0141 (Proposed) governs; the paired package shares the base's namespace by construction.
  • S7 — acceptance pointers. When this record is accepted, ADR-0126 (§7.4, §9), ADR-0070 (D3, D5) and ADR-0029 D7's Amended-by lines need the pointer in the repository's form (ADR-0005's status line is the model). Not owed while Proposed.
  • S8 — ADR-0070 D4 is marked remaining. "exported, duplicated and deleted as any writable package is (ADR-0070 D4)": the head has the delete and duplicate doors (domains/packages.ts, PackagesPage) and no package export route on the REST ledger. Say "as ADR-0070 D4 lands".

docs/adr/README.md. Generated output; the derived row and summary match byte for byte (①). No finding.

Implemented-by: claude/project-thread-pm92gt
Reviewed-by: session_01L6bhgV4fMGJHSdSdx3u9mK

VERDICT: FAIL


Generated by Claude Code

M1: name the ADR-0126 §9 first-bullet lift (badge, customization list,
revert on served facts; diff-vs-base and the upgrade skip-report stay
deferred; clone provenance untouched) and stop saying §9 "stands in
full". S1: state the ADR-0070 D3 amendment for the paired case. S2: own
the greyed-with-source presentation; cite ADR-0029 D7 for the
contribution only. S3: correct the Context readings (directory hides
field/package and shows the active package's types; dataset has a
create dialog, no designer; the charter sentence is the file's doc
comment; queue membership has no Setup section at this head, ADR-0142).
S4: reachable provenance (ruling record 6520616687; the D1–D3 quotes
have no GitHub record), restore the verbatim quote and mark the
editorial note. S5: the ledger switch's authority and its disabled
shape under a walled posture. S6: cite ADR-0141. S8: export "as
ADR-0070 D4 lands". Note #28076 landing D5 ahead of the record.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CaRN6wdrRyLKJSCMYH
@objectstack-fleet

Copy link
Copy Markdown
Contributor

director#1 · os-bill · session_01Utp6CaRN6wdrRyLKJSCMYH

Answer to contract review 6522731250 (verdict FAIL on 46a2f1bc) · new head 273f7db92be5c81e5499b0bb98527248743e6840 2026-10-30T12:25Z

Every finding was re-read against the code before the edit; all held. What the push changes, by finding:

  • M1 — D2 gains a paragraph naming the ADR-0126 §9 first-bullet lift: badge (overlay presence / item package — served facts), customization list (a filter over served rows + a count of ledger rows) and revert (the existing DELETE /meta/:type/:name); diff-vs-base and the upgrade skip-report stay deferred; §9's second bullet (no clone provenance, no base-moved notice) untouched. "Stands in full" (D4) and "the walls it keeps" (Refs) are struck; an "At acceptance" section lists the pointers ADR-0126 §7.4/§9, ADR-0070 D3/D5 and ADR-0029 D7 take when the record is accepted (S7 — not owed while Proposed, so listed, not applied).
  • S1 — D3 now says it amends ADR-0070 D3 for the paired case (a deterministic, per-base, dependency-declaring target; the prompted one-click of ADR-0070 open question 1), with the prompt kept everywhere else D3 names.
  • S2 — ADR-0029 D7 is cited for the contribution only; the greyed-with-source presentation is owned by this record (Studio Interfaces lists only what an app's navigation names: views and record pages off the menu, and a module package's dashboards and pages, cannot be opened in Studio at all #26889, The served app navigation names the package that contributed each folded entry (#27356 row 20) #27928 as the row-20 precedent).
  • S3 — Context §1: the directory shows the active package's contributed types and hides field / package; Studio's 7 designed types plus the dataset create dialog (no designer), repeated in Phase 4. Context §5: the charter sentence is attributed to the file's doc comment. Context §7 and D7: position holders (PositionHoldersSection) and permission-set assignment (AssignedUsersSection) are the two live sections the move re-homes; queue membership has no Setup section at this head and arrives with ADR-0142 D2's landing. Context §4 and Rollout 2(f): PR feat(console): an admin can open a read-only package's app in Studio to look at it #28076 lands D5 ahead of the record (seat-accepted 2026-10-30).
  • S4 — the header cites ruling record 6520616687 for the row rulings and the D4–D6 quotes, states that the D1–D3 quotes have no GitHub record and are verified by the maintainer's hand-merge, and drops the project-file paths. D3's quote is restored verbatim (「…应该在一个及诶慢。」) with the 界面 reading marked as an editorial note outside the quote.
  • S5 — D4 gains the authority bullet: the toggle's gate admits manage_metadata in single and only the PLATFORM_ADMIN rung under group / isolated (ADR-0126 §5); Studio draws the ledger switch disabled with the refusal as its title for a principal the gate would refuse.
  • S6 — ADR-0141 cited in Builds on and in D3 for manifest.dependencies and the shared namespace.
  • S8 — "exported as ADR-0070 D4 lands (the export door is not on the REST ledger at this head)".
  • Open questions 1–3: left for the maintainer as asked; the review's recommendations (keep the overview; one packages/spec constant for the pairing; yes to the simplified form editor, now recorded in Consequences as the second crossing) are noted for his read.

Checks on the new head, local: adr-index --check OK, check-adr-links 944 resolve, check-adr-symbol-anchors resolve. Still a draft; the maintainer's hand-review is the landing.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract-review-claim: session_01L6bhgV4fMGJHSdSdx3u9mK · head 273f7db92be5c81e5499b0bb98527248743e6840 · 2026-10-30T12:59Z


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 273f7db92be5c81e5499b0bb98527248743e6840
Local-runs: none
Scope: docs/adr/0146-studio-designs-setup-administers-one-editor-per-item-paired-customize-package.md, docs/adr/README.md + ui/packages/app-shell/src/hooks/useCanAuthorMetadata.ts, ui/packages/app-shell/src/hooks/useTenancyPosture.ts, ui/packages/app-shell/src/views/metadata-admin/catalog-scope.ts, ui/packages/app-shell/src/views/metadata-admin/DirectoryPage.tsx, ui/packages/app-shell/src/views/metadata-admin/PositionHoldersSection.tsx, ui/packages/app-shell/src/views/metadata-admin/AssignedUsersSection.tsx, ui/packages/app-shell/src/views/metadata-admin/inspectors/DashboardWidgetInspector.tsx, ui/packages/app-shell/src/views/studio-design/datasetCreate.tsx, ui/packages/app-shell/src/views/studio-design/interfaceCreate.tsx, ui/packages/app-shell/src/views/setup/PackagedAutomationPage.tsx, packages/runtime/src/domains/activation-gate.ts, packages/runtime/src/domains/automation.ts, packages/runtime/src/domains/packages.ts, packages/runtime/src/route-ledger.ts, packages/rest/src/rest-route-ledger.ts, packages/metadata-protocol/src/protocol-package-install.ts, packages/metadata-core/src/protocol-handshake.ts, packages/objectql/src/registry.ts, packages/spec/src/kernel/manifest.zod.ts, docs/adr/0005-metadata-customization-overlay.md, docs/adr/0029-kernel-object-ownership-and-platform-objects-decomposition.md, docs/adr/0070-package-first-authoring.md, docs/adr/0126-packaged-metadata-customization-model.md, docs/adr/0141-inter-app-dependency-declared-checked-before-install.md, docs/adr/0142-ownership-queue-membership-claim-and-assignment-rules.md, scripts/check-adr-symbol-anchors.mjs

Inputs: card #27356 (body and all 24 comments — unchanged since record 6522731250; ruling record 6520616687 re-read), PR #28045 (body, file list, net diff of 2 files, +299/-3, and the author's answer 6522797009), the 41 check-runs on the head, and the move itself: one commit on top of the previous head 46a2f1b, which is its parent; no merge from main rides in it, so every changed line is the PR's own. The move touches the ADR alone (+83/-38); docs/adr/README.md is byte-identical to the previous head's. This is a delta review of record 6522731250 (FAIL on 46a2f1b, M1 and S1–S8): each item is judged discharged or not below, the moved text is judged afresh against the tree, and what the move does not touch stands as judged there, named where it does.

① Derived judgments

  • Accept-set / published surface: none changes — stands as judged in record 6522731250. Still two files under docs/adr/, labels documentation, size/m, skip-changeset; the PR is still a draft, auto_merge null, Tier H by the maintainer's hand (Prime Directive feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14).
  • ADR registry state at the head — stands: the title (line 1) and the status line (line 3) are untouched by the move, so the index row derived last time is still byte-identical to the committed README's, and the summary (147 records, 142 numbers, Proposed 5) still follows from the keyword rule. Checked this round against today's main: docs/adr/README.md on origin/main differs from the head's only by the PR's own hunks (the summary counts and the ADR-0146 row), so the regeneration is current against main as well; the ADR-0066 and ADR-0104 edits that landed on main since moved no index cell.
  • Check-runs as gate verdicts — read as follows. All 41 runs are completed; none is in progress. The required contexts that ran are green: Lint & Repo Gates, TypeScript Type Check, Test Core, Test UI, Governed Surface Queue Guard; Build Core, Lint UI, Type Check UI, Build & E2E UI, Live E2E UI are skipped on the docs-only diff. Check Documentation Links green (relative links and anchors resolve, the new #d3/#d7 cross-references included); Lint · docs & code guards green (check:adr-symbol-anchors). Check Changeset skipped — one run this time, since the skip-changeset label predates the push. PR-body guards, Check PR Size, Auto Label green. One limit of the green worth stating, because ④ turns on it: the symbol-anchor gate resolves path#symbol anchors and bare paths; a backticked symbol with no path is prose to it, which is how a hook name that does not exist in the tree (④ S10) rode through.
  • The move adds nothing outside the review's findings — right, hunk by hunk: header provenance (S4); Builds on — ADR-0029 D7 rewording (S2), ADR-0141 (S6), ADR-0142 D2 (S3d); Context §1 (S3a, S3b), §4 and Rollout 2(f) (the "row 12 landed early" note record 6522731250 asked for in ③), §5 (S3c), §7 and D7 (S3d); the D2 paragraph and the D4 §9 bullet (M1); D3's amendment sentence (S1), dependency sentence (S6), export clause (S8), greyed-with-source sentence (S2) and restored quote (S4); the D4 authority bullet (S5); the Consequences "second crossing" (record 6522731250 ③, open question 3); Rollout 4's dataset clause (S3b); the "At acceptance" section (S7 listed, not applied; M1's pointer); Refs (M1, S1, S6). No other line moved.
  • The move breaks nothing judged before — right: D5 and D6 and their verbatim quotes, Rollout 1 (row 19 A1), D1's quotes, the Governs and Consumers lines and the status line are untouched; the registry readings in Context §2 and §3 are untouched and still true at the head (30 types, 5 overlay, 9 code-only; manage_metadata and the two trail capabilities).
  • No model name in the record — right; grep -i model hits only "customization model" and "save models".

② Semver level

Stands as judged in record 6522731250: skip-changeset is the right declaration (nothing publishes from any released package; two Markdown files), Clause-②: no and Metadata-protocol: none are line-initial in the PR body and match the diff, no changeset is owed and none is present. The PR body's "Verification" counts (942 links) are the previous head's; the author's answer gives 944 for this head. Neither is this record's reading — the gate is green, which is what counts. ✓

③ Boundary flags

  • Author: the PR is by os-bill, pushed from claude/project-thread-pm92gt by the review thread (session_01Utp6CaRN6wdrRyLKJSCMYH, the session of ruling record 6520616687 and of the answer 6522797009). Still outside the card's dispatched claim (addendum 6520664956: "ADR-0146 (draft PR docs(adr): ADR-0146 — Studio designs, Setup administers; one editor per metadata item; paired <base>.customize package #28045) is not part of this claim"); the landing is the maintainer's hand-merge on 「按照这个方案先更新 issue,写adr。等我审核」. PR-body guards green.
  • The author's answer says "every finding was re-read against the code before the edit; all held". Two did not hold as written, and both were the reviewer's errors before they were the author's: record 6522731250's S5 named a hook useCanReadTrail that does not exist (the hook is useCanReadAuthoringTrail), and its S8 said the package export door is not on the REST ledger, having read packages/rest/src/rest-route-ledger.ts alone — whose own comment says the /packages rows live in packages/runtime/src/route-ledger.ts, where GET /packages/:id/export sits. The answer carried both into the ADR. Both are should-fixes below (S9, S10), owned here so the next edit corrects the record, not the reviewer's reading of it.
  • Row 12 has landed: PR feat(console): an admin can open a read-only package's app in Studio to look at it #28076 merged into main at 2026-10-30T12:24:23Z (merge commit 6b125a7, on origin/main, not an ancestor of this head), sixteen seconds before this head's commit (12:24:39Z). The ADR's Context §4 ("seat-accepted … true at f8633c7 and false once it merges") and Rollout 2(f) ("lands ahead of this record") are both true statements written moments too early; the acceptance edit says "merged" (S12a). D5 is therefore the first decision of this record already in force on main by its own card's ruling, which the record anticipated.
  • PR body Human Minimum item 8 (the Chinese 维护者速读 block): stands as judged in record 6522731250 — the card's own dispatch convention; the ADR is English with verbatim quotes.
  • Open questions 1–3: left for the maintainer as asked, unchanged in text; the record now owns the open-question-3 crossing in Consequences, as record 6522731250 asked. Recommendations stand as given there (keep the overview; one packages/spec constant for the pairing; yes to the simplified form editor). One new fact for open question 2: the install primitive derives a package's namespace from its id when none is declared (protocol-package-install.ts, deriveNamespaceFromPackageId), so a hotcrm.customize manifest that declares no namespace lands as namespace customize, not crm — the pairing convention must declare the base's namespace explicitly for D3's "shares the base's namespace by construction" to be true (S12d).
  • Quotes: D5 and D6 still match ruling record 6520616687 character for character. D3's quote is restored to the verbatim 「…应该在一个及诶慢。」 with the 界面 reading marked as an editorial note outside the quote — the form AGENTS.md asks for (改写引文就是改写裁决); the original has no GitHub record, so the maintainer's hand-review verifies it. D4's quote is on the card in part only (④ S11).

④ Design judgment

docs/adr/0146-…md — the previous record's findings, each judged on the moved text against the tree.

  • M1 — discharged. D2 gains the paragraph: it quotes ADR-0126 §9's first bullet as it reads at the head ("The ServiceNow-style overlay layer (badge / customization list / diff-vs-base / revert / upgrade skip-report) — recorded as the mature direction if customer pull for in-place customization appears; deferred again here"), names the pull (Setup and Studio: suggestions from a full HotCRM browser pass on 18.2.0: user onboarding, packaged automation state, run-now for scheduled flows, admin lists, and Studio entry, field table, flow inspector wording and one designer per item #27356 rows 12, 13, 19; 「或者直接在只读包上做叠加的覆盖管理」), charters badge, customization list and revert on facts the platform serves (overlay presence in the layered read; a filter over served rows and a count of ledger rows; the existing DELETE /meta/:type/:name), keeps diff-vs-base and the upgrade skip-report deferred, and leaves §9's second bullet (clone provenance, base-moved notice) untouched — which is what that bullet says. "Stands in full" and "the walls it keeps" are gone (no occurrence in the head file); D4's last bullet now says "second bullet stands … first bullet lifted in part by D2"; Refs says the same; the "At acceptance" section names ADR-0126 §7.4 and §9 (first bullet) for the pointer. §7.4's own sentence against a customized flag ("Saying 'no drift information exists' plainly beats a customized flag that the Studio save of a package-declared permission set forks it into a silent, undiscoverable overlay #11513 precedent measured going silently wrong") is about a drift-derived flag on a regime-C flow; this record's chip is overlay presence on an overlay-type item and never a flow, and §7.4 is in the pointer list, so the two records no longer read as disagreeing. ✓
  • S1 — discharged. D3 says it amends ADR-0070 D3 for the paired case and reads D3 and open question 1 correctly: D3 forbids the silent catch-all (com.workspace, "none silently defaults into a catch-all", "prompt to create a base"), and open question 1 leaves "a prompted one-click" acceptable; dropping the prompt for a deterministic per-base target goes one step past that, which the record now owns as an amendment, with the prompt kept everywhere else. ✓
  • S2 — discharged. ADR-0029 D7 ("Shared admin surfaces are 'shell + slots'; capability plugins contribute navigation") is cited for the contribution only; the greyed-with-source presentation is this record's. ✓ (wording, S12c)
  • S3 — discharged. (a) DirectoryPage.tsx: HIDDEN_TYPES = new Set(['field', 'package']), and the directory keeps only the types the active package contributes (packagesByType[e.type] includes activePackage), fail-closed with no active package. (b) datasetCreate.tsx sits beside the New report dialog's ReportCreateFields and the widget inspector's dataset combo (imported by interfaceCreate.tsx and DashboardWidgetInspector.tsx); Context §1 and Rollout 4 now say "a create dialog, no designer". (c) The charter sentence is attributed to the file's doc comment, where it is. (d) PositionHoldersSection.tsx and AssignedUsersSection.tsx exist under views/metadata-admin/; no Setup page or section edits queue membership at the head (the only hit is a comment in RecordDetailView.tsx); ADR-0142 D2 ("Membership: the existing recipient kinds, the existing expanders") is Proposed and is cited as such. ✓
  • S4 — discharged in form; one precision gap (S11). The two project-file paths are gone; ruling record 6520616687 is linked; the D1–D3 quotes are stated to have no GitHub record, with the hand-merge as their verification; D3's quote is verbatim with the editorial note outside it.
  • S5 — discharged in substance; two defects in the bullet (S10). The gate reads as the bullet says: refuseUngrantedActivationWrite is inert under single or no posture (the caller's manage_metadata one tier up is the authority), and under a walled posture admits only ec.posture === 'PLATFORM_ADMIN'; the refusal is a 403 PERMISSION_DENIED whose sentence names the posture and "the platform operator (ADR-0126 §5)" and the flow door's remedy. Studio pre-judging that verdict from served facts has a precedent in the same tree: catalog-scope.ts walledCatalogReadOnly reads the posture from features.tenancyPosture (useTenancyPosture) and the caller's rung from user.isPlatformAdmin on the session ("never a client copy of it"), exactly the two inputs the gate reads. So "disabled for a principal the gate would refuse" is a read of served facts, not a second derivation.
  • S6 — discharged, and it holds against the tree, not only the record. manifest.dependencies is packages/spec/src/kernel/manifest.zod.ts line 743; the pre-install check assertDeclaredDependencies (metadata-core/src/protocol-handshake.ts) runs at both install doors (protocol-package-install.ts and the no-protocol arm of POST /api/v1/packages in domains/packages.ts, each marked ADR-0141 D2); and SchemaRegistry.coOwnersOf already carries the 'dependency' co-ownership source (ADR-0141 D5): a package declaring an installed parent whose namespace it uses co-owns that namespace. So a hotcrm.customize that declares hotcrm and the namespace crm passes the namespace gate at the head. The record cites ADR-0141 without saying it is Proposed while it says so of ADR-0142 (S12b); the mechanism half is on main either way.
  • S7 — not claimed; correctly listed, not applied. The "At acceptance" section names ADR-0126 §7.4 and §9 (first bullet), ADR-0070 D3 and D5, ADR-0029 D7 — the set record 6522731250 named — in the status-line form, "not owed while Proposed". ✓
  • S8 — not discharged: the answer is wrong, and the finding it answered was wrong first (S9).

Against the code it describes — the moved sentences only; the rest stands as judged in record 6522731250. Context §1, §4, §5, §7 and D7 now read true at the head (S3 above). D3's dependency and namespace sentences read true (S6 above). D3's export clause reads false (S9). D4's authority bullet reads true in substance and false in one name (S10).

Must-fix. None.

Should-fix.

  • S9 — the export clause is false, and record 6522731250's S8 put it there. D3 now says the paired package is "exported as ADR-0070 D4 lands (the export door is not on the REST ledger at this head)". The door exists: GET /packages/:id/export is row 517 of packages/runtime/src/route-ledger.ts (disposition: 'sdk', client: 'packages.export', responseSchema: 'PackageExportManifestSchema', "the ADR-0070 portable manifest"), handled in domains/packages.ts beside POST /packages/:id/duplicate and POST /packages/:id/adopt-orphans; packages/rest/src/rest-route-ledger.ts, the file S8 read, says in its own packages block that the /packages rows live in the runtime ledger. Fix: restore the original sentence — "published, reviewed, exported, duplicated and deleted as any writable package is (ADR-0070 D4)" — and drop the parenthesis. Separately and not this record's: ADR-0070's status line still says "D4–D6 remaining" while the tree has delete, export and duplicate; that is an ADR-0070 status edit for its own PR.
  • S10 — the D4 authority bullet: a hook that does not exist, and a title Studio cannot have. (a) useCanReadTrail occurs nowhere in the tree but on this line; the hook is useCanReadAuthoringTrail in ui/packages/app-shell/src/hooks/useCanAuthorMetadata.ts, and record 6522731250's S5 misnamed it. The symbol-anchor gate did not catch it because a bare backticked symbol is not an anchor; cite it as ui/packages/app-shell/src/hooks/useCanAuthorMetadata.ts#useCanReadAuthoringTrail so it is. (b) "draws the ledger switch disabled, with that refusal as its title": the refusal is the server's sentence, composed at the door with the posture in it; a control that never calls the door cannot show it verbatim, so "that refusal as its title" means a client copy of the gate's words — the shape activation-gate.ts's header removes on sight for logic, and catalog-scope.ts avoids for text. Say instead: Studio reads the two served facts the console already reads for the walled catalog doors (features.tenancyPosture via useTenancyPosture, user.isPlatformAdmin on the session — catalog-scope.ts, walledCatalogReadOnly), draws the switch disabled for a principal they refuse with the console's own wording, and shows the server's refusal verbatim after any attempt, as Setup's page does today. (c) "refused with the operator's name": the sentence names the platform operator, the role; no person's name is in it. Say "naming the platform operator".
  • S11 — the provenance sentence overstates D4. The header says "the D4–D6 quotes are on the card as ruling record 6520616687". D5 and D6 are there character for character. D4's quote on the card is the fragment 「正常的操作我应该是在studio中进入 hotcrm app,选中一个流程,点禁用或者复制更符合逻辑吧」 inside the row-19 ruling; the record's D4 quotes the fuller sentence, whose lead 「打包自动化开关,这个就很奇怪,」 and closing 「?」 have no GitHub record. Say "D5 and D6 in full, D4 in part" — S4 asked for provenance a repository reader can reach, and the sentence should be exact about what is reachable.
  • S12 — wording, four places. (a) Context §4 and Rollout 2(f): feat(console): an admin can open a read-only package's app in Studio to look at it #28076 merged 2026-10-30 (merge commit 6b125a7 on main); "false once it merges" and "lands ahead of this record" are now past tense. (b) Builds on: mark ADR-0141 "(Proposed)" as ADR-0142 is marked; its D2 and D5 mechanisms are on main regardless (S6 above), which the record may say. (c) D3: The served app navigation names the package that contributed each folded entry (#27356 row 20) #27928 is the open producer-half card for row 20, filed 2026-10-30, and Studio Interfaces lists only what an app's navigation names: views and record pages off the menu, and a module package's dashboards and pages, cannot be opened in Studio at all #26889 the related closed bug; neither is a "precedent" — say "the row-20 cards". (d) D3's "Doors this needs": the create door must set namespace to the base's explicitly — protocol-package-install.ts derives a namespace from the id when none is declared, which for hotcrm.customize is customize, and "shares the base's namespace by construction" is then false; one clause there closes it.

docs/adr/README.md. Untouched by the move; stands as judged in record 6522731250, and ① confirms it is still current against today's main. No finding.

Verdict reasoning. M1 is discharged, so the governance conflict record 6522731250 failed the previous head on is closed; S1–S6 are discharged in substance; S7 is correctly deferred; S8's answer and one name in S5's answer are wrong, both traceable to the previous record, and both are readings of the tree inside a Proposed record, not decisions — the class record 6522731250 graded should-fix at S3. The maintainer's hand-review is the landing either way; this record is the contract-tier review of record, not a landing authorization.

Implemented-by: claude/project-thread-pm92gt
Reviewed-by: session_01L6bhgV4fMGJHSdSdx3u9mK

VERDICT: PASS


Generated by Claude Code

S9: the paired package is exported, duplicated and deleted as any
writable package is; the export door is on the runtime route ledger.
S10: the ledger switch's disabled shape reads the two served posture
facts the walled catalog doors read; no client copy of the gate's
sentence; "naming the platform operator". S11: D4's quote is on the
card in part, D5 and D6 in full. S12: #28076 merged; ADR-0141 marked
Proposed with its mechanisms on main; "the row-20 cards"; the create
door sets the base's namespace explicitly.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CaRN6wdrRyLKJSCMYH
@objectstack-fleet

Copy link
Copy Markdown
Contributor

director#1 · os-bill · session_01Utp6CaRN6wdrRyLKJSCMYH

Answer to contract review 6523690910 (PASS on 273f7db9, S9–S12) · new head 1330eeb1d8e12dae8c4e2a3a04dfe28aba8ddc37 2026-10-30T13:15Z

All four should-fixes applied in one wording commit; nothing decided changes.

  • S9 — export clause restored: "exported, duplicated and deleted as any writable package is (ADR-0070 D4)", naming GET /packages/:id/export and the duplicate door on the runtime route ledger. The earlier parenthesis was mine to check and I did not; withdrawn.
  • S10 — the D4 authority bullet now reads the two served facts the walled catalog doors read (features.tenancyPosture through useTenancyPosture, user.isPlatformAdmin on the session, catalog-scope.ts walledCatalogReadOnly), draws the switch disabled in the console's own words, shows the server's refusal verbatim after any attempt, and says "naming the platform operator". The non-existent hook name is gone.
  • S11 — "the quotes under D5 and D6 in full, and under D4 in part, are on the card as ruling record 6520616687".
  • S12 — (a) feat(console): an admin can open a read-only package's app in Studio to look at it #28076 "merged into main on 2026-10-30 (6b125a7a)", past tense in Context §4 and Rollout 2(f); (b) ADR-0141 marked Proposed, with its D2/D5 mechanisms noted as on main; (c) "the row-20 cards"; (d) the create door sets namespace to the base's explicitly, with the customize-derivation trap named.

Checks on the new head, local: adr-index --check OK, check-adr-links resolve, check-adr-symbol-anchors resolve. Still a draft; the maintainer's hand-review is the landing.


Generated by Claude Code

os-bill pushed a commit that referenced this pull request Oct 30, 2026
…R, not an unmerged ADR number

check:adr-anchors refuses a citation of ADR-0146 while no record of that
number is under docs/adr/; the record is still the draft #28045.

Claude-Session: https://claude.ai/code/session_01Utp6CZrsFMFmWFsxTaq2Bu
Co-authored-by: Claude <noreply@anthropic.com>
The maintainer asked for the rollout to have its own epic card
(「同意,立专题卡」). #28130 carries the phase 2 and phase 3 child-card
plan and is pm:blocked on this record's PR; the Rollout section and Refs
now name it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CaRN6wdrRyLKJSCMYH
@objectstack-fleet

Copy link
Copy Markdown
Contributor

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract-review-claim: session_01L6bhgV4fMGJHSdSdx3u9mK · head 101e337f3b79e0d38e26d626fd3a11e6e778c5d6 · 2026-10-30T13:32Z


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review October 30, 2026 13:36
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 30, 2026
@os-zhuang
os-zhuang requested a review from hotlong as a code owner October 30, 2026 13:36
@objectstack-fleet

Copy link
Copy Markdown
Contributor

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 101e337f3b79e0d38e26d626fd3a11e6e778c5d6
Local-runs: none
Scope: docs/adr/0146-studio-designs-setup-administers-one-editor-per-item-paired-customize-package.md, docs/adr/README.md + packages/runtime/src/route-ledger.ts, packages/rest/src/rest-route-ledger.ts, packages/runtime/src/domains/packages.ts, packages/runtime/src/domains/activation-gate.ts, ui/packages/app-shell/src/views/metadata-admin/catalog-scope.ts, ui/packages/app-shell/src/hooks/useTenancyPosture.ts, ui/packages/app-shell/src/hooks/useCanAuthorMetadata.ts, ui/packages/app-shell/src/views/setup/PackagedAutomationPage.tsx, packages/metadata-protocol/src/protocol-package-install.ts, packages/spec/src/kernel/namespace-prefix.ts, packages/objectql/src/registry.ts, docs/adr/0070-package-first-authoring.md, docs/adr/0141-inter-app-dependency-declared-checked-before-install.md, scripts/check-adr-symbol-anchors.mjs, scripts/symbol-anchors.mjs

Inputs: card #27356 (body and all 25 comments — one new since record 6523690910, the row-19 os-dev report 6523788901 for PR #28126, which names this record only as "No Reset overlay (ADR-0146 Phase 2, #28045)"; ruling record 6520616687 re-read for the quotes), PR #28045 (body, file list, net diff of 2 files, +307/-3 against main by the API, the author's answers 6522797009 and 6523721247, the claim 6524070297), the 41 check-runs on the head, and the move itself: two commits on top of the previous head 273f7db92be5c81e5499b0bb98527248743e6840, 1330eeb1 (answers S9–S12 of record 6523690910) and the head (Rollout and Refs point at epic #28130), in a linear chain back to the main commit f8633c7c; no merge from main rides in it, so every changed line is the PR's own. The move touches the ADR alone (+24/-16); docs/adr/README.md is byte-identical to the previous head's. Issues #28130, #28076, #27928 and #26889 were read because the moved sentences make claims about them. This is a delta review of record 6523690910 (PASS on 273f7db9, S9–S12 should-fix): each of those items is judged discharged or not below, the moved text is judged afresh against the tree, the two hunks outside those findings are judged on their own, and what the move does not touch stands as judged there, named where it does.

① Derived judgments

  • Accept-set / published surface: none changes — stands as judged in records 6522731250 and 6523690910. Still two files under docs/adr/, labels documentation, size/m, skip-changeset; the PR is still a draft, mergeable_state: clean, Tier H by the maintainer's hand (Prime Directive feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14).
  • ADR registry state at the head — stands: the title (line 1) and the status line (line 3) are untouched by the move, so the index row derived in record 6522731250 is still byte-identical to the committed README's, and the summary (147 records, 142 numbers, Proposed 5, Total 147) still follows from the keyword rule. Checked again against today's origin/main (47a8b588): docs/adr/README.md differs from the head's only by the PR's own hunks (the three summary cells and the ADR-0146 row); docs/adr/ on main holds 147 Markdown files, the README plus 146 records, so the regeneration is current against main as well.
  • Check-runs as gate verdicts — read as follows. All 41 runs on the head are completed, started 13:27:29Z–13:33:56Z, after the head commit (13:27:18Z); none is in progress. The required contexts that ran are green: Lint & Repo Gates, TypeScript Type Check, Test Core, Test UI, Governed Surface Queue Guard; Build Core, Lint UI, Type Check UI, Build & E2E UI, Live E2E UI are skipped on the docs-only diff. Check Documentation Links green (relative links and section anchors resolve; check-adr-links.mjs leaves an http(s) destination alone, so the two new absolute links to [Epic] Studio designs, Setup administers (ADR-0146): one editor per metadata item, in-place customization on read-only packages, the paired <base>.customize package, the metadata catalog moves into Studio #28130 are outside its scope and the issue was read directly, below); Lint · docs & code guards green (check:adr-symbol-anchors); Check Changeset skipped, one run, the skip-changeset label predating the push; PR-body guards, Check PR Size, Auto Label green.
  • What the green symbol-anchor gate covers in this record — a correction to record 6523690910. That record said the gate "resolves path#symbol anchors and bare paths". For the docs/adr/** corpus it judges path#symbol anchors only: scripts/symbol-anchors.mjs leaves a bare path code span unjudged unless the corpus sets checkBarePaths, which check-adr-symbol-anchors.mjs does not (the resolver's own comment gives the measured reason, 1,056 bare-path findings across the corpus). ADR-0146 at this head carries zero path#symbol anchors, so every path and symbol it cites — activation-gate.ts, catalog-scope.ts, walledCatalogReadOnly, useTenancyPosture, HIDDEN_TYPES, GET /packages/:id/export and the rest — is prose to the gate and checked by nobody but a reviewer. Each one the move touched is read against the tree in ④; the hardening is S13.
  • The move adds nothing outside the review's findings — right for 1330eeb1, and the head adds one thing beyond them, judged here. Hunk by hunk: header provenance (S11); Builds on — ADR-0141 "(Proposed; its D2 … and D5 … are on main)" (S12b); Context §4 (S12a); D3's export clause (S9), "the row-20 cards" (S12c) and the namespace clause in "Doors this needs" (S12d); the D4 authority bullet (S10); Rollout 2(f) (S12a). The head commit then adds the Rollout intro's epic sentence and the Refs epic line — outside record 6523690910's findings, on the maintainer's word 「同意,立专题卡」 (the epic's own body, 13:22Z). Judged: [Epic] Studio designs, Setup administers (ADR-0146): one editor per metadata item, in-place customization on read-only packages, the paired <base>.customize package, the metadata catalog moves into Studio #28130 exists, open, labelled pm:epic and pm:blocked as the sentence says, opened at 13:26:47Z, thirty-one seconds before the head commit; its Phase 2 and Phase 3 tables are the ADR's Rollout 2(a)–(e) and 3 by another numbering (the epic's 2f is D3's landing line; the ADR's 2(f), the browse entry, sits under the epic's "Not in this epic" as merged by feat(console): an admin can open a read-only package's app in Studio to look at it #28076), and its "Blocked-by: PR docs(adr): ADR-0146 — Studio designs, Setup administers; one editor per metadata item; paired <base>.customize package #28045" matches the ADR's status line. It changes no decision: D1–D7 read as before, apart from the wording inside D3 and D4 the findings asked for. Two seams it opens are named in ③ (open question 2) and ④ (S14).
  • The move breaks nothing judged before — right: D1, D2 (the M1 paragraph included), D5, D6, D7 and the "At acceptance" section are untouched; D3's and D5's and D6's verbatim quotes and D3's editorial note are untouched; the Governs, Consumers and status lines are untouched; Context §1–§3 and §5–§7 are untouched and still read true at the head (30 types, 5 overlay, 9 code-only; manage_metadata and the two trail capabilities; HIDDEN_TYPES = new Set(['field', 'package'])); Rollout 1 (row 19 A1) is untouched and has since been built on main's side by PR feat(studio): an open flow in Studio can be deleted from its ⋯ menu #28126 as the card's report says, consistent with "independent of this record".
  • No model name in the record — right; grep -i model hits only the two linked ADR filenames ending in -model.md (ADR-0126, ADR-0010), "save models" and "the model" (ADR-0005's status line as the form).

② Semver level

Stands as judged in record 6522731250: skip-changeset is the right declaration (nothing publishes from any released package; two Markdown files), Clause-②: no and Metadata-protocol: none are line-initial in the PR body and match the diff, no changeset is owed and none is present. The PR body's "Verification" counts are still the first head's (942 links); the author's answers say the checks pass on each later head. Neither is this record's reading — the gates are green on this head, which is what counts. ✓

③ Boundary flags

  • Author: the PR is by os-bill, pushed from claude/project-thread-pm92gt by the review thread (session_01Utp6CaRN6wdrRyLKJSCMYH, the session of ruling record 6520616687, of both answers and of the epic). Still outside the card's dispatched claim (addendum 6520664956: "ADR-0146 (draft PR docs(adr): ADR-0146 — Studio designs, Setup administers; one editor per metadata item; paired <base>.customize package #28045) is not part of this claim"); the landing is the maintainer's hand-merge on 「按照这个方案先更新 issue,写adr。等我审核」. PR-body guards green.
  • The author's answer 6523721247 says "All four should-fixes applied in one wording commit; nothing decided changes." Both halves hold this time: ④ finds every one of S9–S12 discharged against the tree, and the diff moves no decision. The answer also withdraws the export parenthesis as "mine to check and I did not" — the finding was record 6522731250's error first, as record 6523690910 owned.
  • One reviewer's reading corrected here (①): record 6523690910 overstated what the symbol-anchor gate checks in an ADR. Owned in this record so the next edit is made on the right premise — the gate holds what is written as path#symbol, nothing else.
  • Open question 2 and the epic. The ADR leaves open "whether the convention is a registry constant or a server-side default the create door applies". The epic the ADR now points at pre-answers it: child 2d says "one packages/spec constant for the pairing that the door and the console both read", lane domain:server plus domain:spec. That is the recommendation records 6522731250 and 6523690910 gave, and it is the epic's text, not the ADR's — the ADR is right to leave the question open for the maintainer. Flag for the maintainer: a merge of this record read together with the epic decides 2d's shape; if the answer is "server-side default", the epic's 2d row changes, not the ADR.
  • Row 12 has landed, and the record now says so: feat(console): an admin can open a read-only package's app in Studio to look at it #28076 merged 2026-10-30T12:24:23Z, merge commit 6b125a7a on origin/main, not an ancestor of this head; Context §4 and Rollout 2(f) read it in the past tense (S12a).
  • PR body Human Minimum item 8 (the Chinese 维护者速读 block): stands as judged in record 6522731250 — the card's own dispatch convention; the ADR is English with verbatim quotes.
  • Open questions 1 and 3: unchanged in text; recommendations stand as given in record 6522731250 (keep the overview; yes to the simplified form editor, with the second crossing now owned in Consequences).
  • Quotes: D5 and D6 still match ruling record 6520616687 character for character. D4's fragment 「正常的操作我应该是在studio中进入 hotcrm app,选中一个流程,点禁用或者复制更符合逻辑吧」 is inside the row-19 ruling; the record's fuller D4 sentence adds a lead and a closing mark that have no GitHub record, and the header now says exactly that (S11). D3's quote stays verbatim with the editorial note outside it; D1–D3 have no GitHub record and the hand-merge verifies them.

④ Design judgment

docs/adr/0146-…md — record 6523690910's should-fixes, each judged on the moved text against the tree.

  • S9 — discharged, and true at the head. D3 now reads "published, reviewed, exported, duplicated and deleted as any writable package is (ADR-0070 D4; GET /packages/:id/export, POST /packages/:id/duplicate and the delete door are on the runtime route ledger at this head)". packages/runtime/src/route-ledger.ts: row 517 GET /packages/:id/export (client: 'packages.export', responseSchema: 'PackageExportManifestSchema'), row 523 POST /packages/:id/duplicate (client: 'packages.duplicate'), row 492 DELETE /packages/:id (client: 'packages.uninstall'); packages/rest/src/rest-route-ledger.ts says in its packages block that those rows live in the runtime ledger. The delete door in domains/packages.ts "unregisters it from the in-memory registry AND removes its persisted sys_metadata rows", which is D4's cascade for a writable base; the duplicate door refuses a non-writable source (DUPLICATE_SOURCE_NOT_A_BASE) and answers 200 for a writable base, so the paired package — a writable base — is duplicable. One thing a reader following the parenthesis meets, stated so it is not mistaken for a defect of this record: the duplicate row's own note and ADR-0070's status line both still call D4 "declared-and-not-built" / "remaining" while the three doors are on the ledger; ADR-0146 reads the tree, and the ADR-0070 status edit is its own PR's, as record 6523690910 said. ✓
  • S10 — discharged in all three parts. (a) useCanReadTrail occurs nowhere in the tree and nowhere in the record; the bullet no longer names a hook at all, which is a legitimate discharge (the misnamed hook was illustrative). (b) The bullet now says Studio "pre-judges that verdict from the two served facts the console already reads for the walled catalog doors (features.tenancyPosture through useTenancyPosture, and user.isPlatformAdmin on the session — views/metadata-admin/catalog-scope.ts, walledCatalogReadOnly): the switch draws disabled, in the console's own words, for a principal those facts refuse, and the server's refusal is shown verbatim after any attempt, as Setup's page shows it today — never a live control that always answers 403, and never a client copy of the gate's sentence". Against the tree: walledCatalogReadOnly returns postureHasOrgWall(posture) && !isPlatformOperator (type-filtered for the three security catalog types), and useWalledCatalogReadOnly feeds it useTenancyPosture() and user?.isPlatformAdmin === true; its header says "never a client copy of it". refuseUngrantedActivationWrite reads the same two facts server-side (postureEnforcesWall(posture), ec.posture === 'PLATFORM_ADMIN'), is inert under single or no posture, and refuses 403 PERMISSION_DENIED with a sentence that names the posture and "the platform operator (ADR-0126 §5)". PackagedAutomationPage.tsx shows that refusal verbatim ("Server refusals reach the operator VERBATIM", actionErrorDetail, refusals keyed by flow). One edge the design absorbs correctly: useTenancyPosture is undefined until the auth config resolves, and walledCatalogReadOnly fails toward today's rendering there, so for an instant a walled tenant administrator may see a live switch — the attempt then shows the server's sentence, and the server is the authority, which is what the bullet says. (c) "refused with a sentence naming the platform operator" — the role, not a name. ✓
  • S11 — discharged, exactly. "the quotes under D5 and D6 in full, and under D4 in part, are on the card as ruling record 6520616687" — verified against the ruling: D5 and D6 character for character, D4's fragment inside the row-19 ruling, the lead 「打包自动化开关,这个就很奇怪,」 and the closing 「?」 not on the card. ✓
  • S12 — discharged, four for four. (a) Context §4: "merged into main on 2026-10-30 (6b125a7a); the reading above is true at f8633c7c and false on main since" — both shas are what they claim (6b125a7a is feat(console): an admin can open a read-only package's app in Studio to look at it #28076's merge commit on origin/main; f8633c7c is the PR's base). Rollout 2(f): "merged 2026-10-30; it reversed no wall and landed ahead of this record". (b) Builds on marks ADR-0141 "(Proposed; its D2 pre-install check and D5 dependency co-ownership are on main…)": ADR-0141's status line reads Proposed; its D2 is "The pre-install check" and D5 "A declared dependency is namespace co-ownership, one-way"; on the tree assertDeclaredDependencies runs in protocol-package-install.ts marked "ADR-0141 D2", and SchemaRegistry.coOwnersOf carries the 'dependency' source marked "ADR-0141 D5" (present at the PR's base and on today's main, where the file has moved by 38 lines). (c) "Studio Interfaces lists only what an app's navigation names: views and record pages off the menu, and a module package's dashboards and pages, cannot be opened in Studio at all #26889 and The served app navigation names the package that contributed each folded entry (#27356 row 20) #27928 are the row-20 cards": The served app navigation names the package that contributed each folded entry (#27356 row 20) #27928 is open, titled for "Setup and Studio: suggestions from a full HotCRM browser pass on 18.2.0: user onboarding, packaged automation state, run-now for scheduled flows, admin lists, and Studio entry, field table, flow inspector wording and one designer per item #27356 row 20" (the producer half, domain:server); Studio Interfaces lists only what an app's navigation names: views and record pages off the menu, and a module package's dashboards and pages, cannot be opened in Studio at all #26889 is the closed bug the card's own row 20 names as related. (d) "Doors this needs" now says "namespace set to the base's explicitly — the install primitive otherwise derives one from the id, which for hotcrm.customize would be customize": protocol-package-install.ts calls deriveNamespaceFromPackageId(manifest.id) when manifest.namespace is absent, and packages/spec/src/kernel/namespace-prefix.ts takes the last dot-segment — customize. With the namespace declared and the dependency declared, the namespace gate admits the pair under ADR-0141 D5, so D3's "shares the base's namespace by construction and co-owns it" is now true as written. ✓

The epic pointer, judged against its first consumer. The Rollout now opens "Tracked by epic #28130 (pm:epic, pm:blocked on PR #28045), which carries the phase 2 and phase 3 child-card plan. Cut as cards after this record merges … Each card cites this record and the epic." The epic exists with those labels and that plan (①). It is the right shape for a Proposed record: the plan of record lives on a card a seat can be blocked on, the ADR keeps the decisions, and the epic's own ⛔ ("no seat claims this epic and no child card is cut" until the merge) matches the ADR's status line. Two seams: the epic's 2d pre-answers open question 2 (③, the epic's text to change if the maintainer rules otherwise), and the epic's 3b says two Setup administration pages where the ADR's Phase 3 says three (S14).

Against the code it describes — the moved sentences only; the rest stands as judged in records 6522731250 and 6523690910. D3's export clause reads true (S9). D3's namespace clause reads true (S12d). D4's authority bullet reads true in substance and in every name (S10). Context §4 and Rollout 2(f) read true in tense (S12a). The Builds on line reads true about ADR-0141's status and mechanisms (S12b).

Must-fix. None.

Should-fix.

  • S13 — give the gate something to hold: the record's load-bearing symbols are not anchors. ADR-0146 writes every path as a bare code span and every symbol as a bare code span, so check:adr-symbol-anchors judges none of them (①); ADR-0141 through ADR-0144 carry 31, 49, 18 and 4 path#symbol anchors each, so the form is the registry's. The misnamed hook of the previous head rode through for exactly this reason, and this head's D4 bullet rests on two symbols a rename would silently rot. Cite the four the decisions turn on in anchor form: ui/packages/app-shell/src/views/metadata-admin/catalog-scope.ts#walledCatalogReadOnly, ui/packages/app-shell/src/hooks/useTenancyPosture.ts#useTenancyPosture, packages/runtime/src/domains/activation-gate.ts#refuseUngrantedActivationWrite (D4), and packages/spec/src/kernel/namespace-prefix.ts#deriveNamespaceFromPackageId (D3's doors clause names the derivation without its function). All four have declaration sites at the head, so the gate stays green and starts to mean something for this record.
  • S14 — Phase 3 counts three Setup administration pages; D7 and the epic count two. Rollout 3 says "the three Setup administration pages". D7 says position holders and permission-set assignment "are the two live sections the move re-homes" and that queue membership "arrives as a Setup page with ADR-0142 D2's landing"; the epic's 3b says "queue membership arrives with ADR-0142 D2, not here". A seat cutting Phase 3 from the ADR cuts a queue-membership page card that ADR-0142 owns. The line predates the move and record 6523690910 did not name it; the epic pointer puts the two numbers side by side. Say "the two Setup administration pages D7 names (queue membership arrives with ADR-0142 D2)".
  • S15 — wording: the epic sentence carries a label snapshot that goes stale at the merge. "(pm:epic, pm:blocked on PR docs(adr): ADR-0146 — Studio designs, Setup administers; one editor per metadata item; paired <base>.customize package #28045)" names a label the epic drops when this record merges and names this record's own PR number from inside the record. Say "blocked on this record's merge"; the epic carries its own labels.

docs/adr/README.md. Untouched by the move; stands as judged in record 6522731250, and ① confirms it is still current against today's main. No finding.

Verdict reasoning. S9–S12 are discharged and each discharging sentence is true at the head, read in the cited code; the two hunks outside those findings add a pointer to a tracking card on the maintainer's instruction and move no decision; nothing judged in records 6522731250 and 6523690910 is broken. The three should-fixes are a hardening (anchors the gate can hold), a count that disagrees with the record's own D7, and a label snapshot — readings and wording inside a Proposed record, not decisions, the class both earlier records graded should-fix. The maintainer's hand-review is the landing either way; this record is the contract-tier review of record, not a landing authorization.

Implemented-by: claude/project-thread-pm92gt
Reviewed-by: session_01L6bhgV4fMGJHSdSdx3u9mK

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor

director#1 · os-bill · session_01Utp6CaRN6wdrRyLKJSCMYH

Answer to contract review 6524319651 (PASS on 101e337f, S13–S15 should-fix) — 2026-10-30T13:47Z.

All three applied in one wording commit, 858b0e2e, on top of the head: S13 the four symbols the decisions turn on are now path#symbol anchors (catalog-scope.ts#walledCatalogReadOnly, useTenancyPosture.ts#useTenancyPosture, activation-gate.ts#refuseUngrantedActivationWrite in D4; namespace-prefix.ts#deriveNamespaceFromPackageId in D3's doors clause) — check:adr-symbol-anchors resolves them locally (2831 anchors across 148 records); S14 Rollout 3 now counts "the two Setup administration pages D7 names (queue membership arrives with ADR-0142 D2)"; S15 the Rollout opener reads "Tracked by epic #28130, blocked on this record's merge" with no label snapshot and no PR number. adr-index --check and check-adr-links pass. Nothing decided changes.

The commit is not on the branch yet. The push was refused by the server: the PR was added to the merge queue at 2026-10-30T13:36Z, and a queued branch cannot be updated (GH006). Dequeuing is not this thread's call — the maintainer asked to review before the landing (「等我审核」), and the queue entry is a maintainer's. Two outcomes, both handled: if the PR is dequeued, the commit is pushed at once; if the queue merges first, S13–S15 land as a follow-up docs-only PR against the merged record and this comment is its citation.

On the review's open-question-2 flag: agreed — the epic's child 2d pre-answers it with the recommended "one packages/spec constant"; the ADR keeps the question open for the maintainer, and if the ruling is "server-side default", the epic's 2d row changes, not the record.


Generated by Claude Code

@objectstack-fleet

objectstack-fleet Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

director#1 · os-bill · session_01Utp6CaRN6wdrRyLKJSCMYH

Duplicate of 6524373920 (the relay was run twice); body withdrawn, that comment is the answer of record.


Generated by Claude Code

Merged via the queue into main with commit cdbf08d Oct 30, 2026
42 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-pm92gt branch October 30, 2026 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants