You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
security: externalSharingModel is declared and authored but has no runtime reader — an object's externalSharingModel: private changes no answer for any principal (ADR-0090 D11 declared-not-enforced) #23268
Filing gate ①, class (b): a declared key with no runtime reader (ADR-0049 enforce-or-remove).
reach: a named producer: examples/app-showcase's showcase_announcement declares externalSharingModel: private. On a real dogfood boot, ADR-0138 E1's dev measured a guest with a bound read set reading every row of a public_read object whether or not it declares externalSharingModel: private or public_read (2/2 either way; hand-off 6145588579, row_scope_measured). The dev's source reading: the key's only reader is the authoring posture gate.
Filed by domain:engine seat 1 (#6367) · os-support-ai · session_01P88ehrZ6hM19hBzdpsQTTo, from E1's hand-off (the second entry of out_of_scope_findings). ⛔ Not a claim; grading and routing are triage's.
Reader who acts: the lane that owns the key. packages/spec declares it (ADR-0090 D11), and its enforcement would live in the row pipeline (plugin-security / core). Triage routes it. Its direction depends on #23266: option B there gives the key a guest reader, while A or C leave it with no reader for the guest. Either way the question for external principals stays open: enforce or remove.
Governing text
ADR-0090 D11: "OWD gains an external dimension: optional externalSharingModel, default …" and "sharingModel stays scalar and internal; an optional externalSharingModel is added …".
MCP search_issues on this repo, closed included: "externalSharingModel runtime reader declared not enforced external sharing model enforcement" gives 8 hits.
Triage: first grade, bug · security · priority:p2 · target:v18 · domain:spec · area:access, pm:blocked on #23266 (finding removed). externalSharingModel is enforced or removed (ADR-0049), after the guest row scope is ruled
Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-13T14:10Z. ⛔ Not a claim, ⛔ not a dispatch.
Re-checked on origin/main (c0569797a6): outside packages/spec, externalSharingModel appears in non-test source only as a comment in packages/plugins/plugin-audit/src/objects/sys-comment.object.ts:34. There is no runtime reader. The positive control finds sharingModel across plugin-security's source. No runtime principal kind is external today, so the only principal the key could govern is the guest.
Why target:v18: if the answer is removal, that is a breaking narrowing of the authoring surface and should ride the major.
Lane: the key is a packages/spec contract (ADR-0090 D11), so domain:spec decides enforce-or-remove. Removal narrows, so Clause-②: no, with contract-tier review.
Triage: lands in packages/spec (the key) and, under B only, the row pipeline through E1 (domain:spec); rationale: a declared security key with no reader.
Unlock scan: pm:blocked → pm:queue. #23266 is ruled C, so the guest never reads externalSharingModel. What remains is ADR-0049 enforce-or-remove for external principals, which the spec seat decides
Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-13T15:00Z. ⛔ Not a claim, ⛔ not a dispatch.
Ruling C (6148183570): the guest takes neither sharingModel nor externalSharingModel. ADR-0090 D9 and D11 are unchanged, so D11 still declares the key as the OWD for external principals (a portal or partner identity pool).
What this leaves: no runtime principal on main is external (re-checked last round), so the key has nothing it could govern. It is still a security-looking key that an author can set and that changes no answer. showcase_announcement declares externalSharingModel: private.
Direction, for the spec seat:
Measure, then pick one route. Enforce: there is no principal to enforce against. Remove: that changes D11's text, a Tier H sentence in the maintainer's hand. Or a third reading the seat can source.
Any route that changes an accepted record's text goes to the maintainer as a [Decision] card with the workload of each option. ⛔ This seat does not choose it.
Grade: p2 securitytarget:v18, kept. A removal is a breaking narrowing and should ride the major.
ADR-0090 D11(docs/adr/0090-permission-model-v2-concept-convergence.md:460-479):"sharingModel stays scalar and internal; an optional externalSharingModel is added with the same canonical enum, default private." 以及 "external visibility = own records + explicit shares + external OWD"。
ADR-0095(docs/adr/0095-authz-kernel-tenant-layer-and-posture-ladder.md:30-31, :186,状态为已接受、已实施):"EXTERNAL rung's semantics are defined and test-locked now (explicit shares only, never OWD)";EXTERNAL = 0, // explicitly shared rows only — never OWD-derived visibility。ADR-0099 在其上补了 EXTERNAL 档的分级。
防 AI 犯错: 今天的情况是,AI 作者看到一个安全开关,设成 private,就相信门户用户被挡住了,而没有任何错误会纠正它(警告不是拒绝)。等门户按 ADR-0099 的规则上线,这个开关照样不生效,误信会延续下去。在 A 下,作者在发布时被响亮拒绝,并拿到处方;在 B 下,作者只收到一条警告。
Claim: PM loop round 1
Session: session_01D6F9axfoPzR7t8G1J7Ytzz
Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-23268-retire-external-sharing-model
Worktree: objectstack-issue-23268
Domain: domain:spec
Seat: domain:spec#3 (seat post #18883)
File surface (at origin/maine4ec6ec1a or later; stop on a breach and explain it in the report): packages/spec/src/data/object.zod.ts (the key) and its tests; packages/spec/liveness/object.json (the row); packages/spec/src/migrations/entries/** (one ADR-0087 retirement entry, class by census) and the registries they feed; packages/spec/src/kernel/execution-context.zod.ts (one mention); packages/spec/src/ai/solution-blueprint*; packages/lint/src/validate-security-posture.ts, packages/lint/src/rule-explanations.ts, packages/lint/src/authoring-rules.ts (the alias rule and security-external-wider-than-internal); packages/plugins/plugin-security/src/object-posture-gate.ts (the external branch); packages/metadata-core/src/object-schema-fls-references.ts (keep table); comment lines in packages/metadata-protocol/src/protocol.ts, packages/plugins/plugin-audit/src/objects/sys-comment.object.ts, packages/plugins/plugin-sharing/src/sharing-service.ts; examples/app-showcase/src/** (the two objects, security index/positions, tour doc, coverage); content/docs/permissions/**, content/docs/protocol/objectql/security.mdx and the regenerated content/docs/references/**; docs/qa/platform-checklist/areas/access-security.json (the external clause); .changeset/23268-*.md. Separately, a second Tier H PR touching only docs/adr/0090-*.md (the D11 sentence). ⛔ Not content/docs/releases/**.
Container & model: M, mode:subagent, model: default (per dispatch-gates --tier: no path-derived mandate; floor · default · ceiling); review before the queue at CONTRACT_REVIEW_TIER (a narrowing: "a card that only NARROWS one is Clause-②: no … and owes one contract-review-tier review before the queue")
Clause-②: no
Responsibility: packages/spec declares the key (ADR-0090 D11) and no row-pipeline code reads it | ADR-0095/0099's EXTERNAL rung (explicitly shared rows only) already covers external visibility, and no runtime principal is external (assemble-execution-context.ts:358 audience: undefined) | object authors reach it; today only examples/app-showcase sets it
Thread-read: 6159300530
Serial constraints cleared: packages/metadata-protocol/src/protocol.ts is in the claimed surfaces of #23297, #23225, #23357 (seat 1, in flight) — this card touches comment lines only; packages/spec/src/kernel/execution-context.zod.ts is in #23287's (seat 2, in flight); examples/app-showcase is in #15204's (epic #15194, in flight); open PR #23368 (plugin-security) shares none of these files. None is a SINGLE_CLAIM_PATHS path; ordinary concurrency, the later lander merges main.
Ruling of record: 6159300530 (letter A, the maintainer's 「23268 同意」); premises re-read on origin/maine4ec6ec1a at 2026-10-14T05:28Z: no row-pipeline reader of the key outside packages/spec, audience: undefined at assemble-execution-context.ts:358, no open PR names the key.
Under the maintainer's grading rule, recorded on #6015 (6163099154, amended 6163539661) and extended today, 「立卡,规则扩到所有车道,REST 收窄也算」: every metadata-protocol change, and every narrowing of a REST or data door, is p1 + target:v18 in every lane. The skill text follows in #23431.
Why it is in the class: ruling A retires externalSharingModel from the object contract (packages/spec/src/data/object.zod.ts). That is an authoring-surface narrowing, and it rides the 18 major.
{
"issue": 23268,
"status": "done",
"branch": "claude/issue-23268-retire-external-sharing-model",
"pr": "https://github.com/objectstack-ai/objectstack/pull/23441",
"session": "session_01D6F9axfoPzR7t8G1J7Ytzz",
"premise_still_valid": true,
"summary": "Retired object.externalSharingModel per ruling 6159300530 letter A: retiredKey() tombstone on ObjectSchema with the delete-the-key prescription, D2 conversion object-external-sharing-model-removed (step 18, order 64, retiredFromLoadPath, retiredAfter 17.7.0), RETIRED_KEYS_BY_MAJOR[18] data/Object:externalSharingModel, D3 object-external-sharing-model-retired plus its STEP18_RATIONALE fragment; ledger row planned to dead (tombstone row kept), lint rule security-external-wider-than-internal and its SECURITY_EXTERNAL_WIDER export removed, posture gate R1 external half removed, both showcase objects, five doc pages plus docs/design, the QA item owd-save-gate (revision 5) and the crossTenantAccess hint no longer name the key; new tree-scoped retirement pin. Census class: registered (Studio offered the select and PUT /meta/object persisted it, so stored object rows can carry it; the replayed strip heals them). Companion Tier H PR #23443 (docs/adr/0090 only, Refs) carries the D11 amendment with the 维护者速读 section. Correction for the seat to apply to PR #23441's body (dev writes the body once): the Evidence bullet's gate line should read 'at 1060ed3e7: 125 derived, 125 run, 124 exit 0; check:platform-checklist exit 1 with the 8 pre-existing findings identical to e4ec6ec1a; --ran 125 accounted, 0 NOT-MEASURED' — the body still says check:dual-build-cjs-loads NOT MEASURED, but it exited 0 on the final-head run.",
"tests": "All at head 1060ed3e7 unless noted (merged origin/main 04a314873 via os-regen-merge.sh). spec: build + check:generated all 14 up to date; local project 3 shards pre-merge 225/7366, 224/6477(1 todo), 224/6619 + 1 red fixed (retired-key-removal-version JOIN) re-run 1/20; post-merge conversions+migrations+object.test+that pin 20/956; repo project 2 shards 30/470 + 29/471 (NOT MEASURED: scripts/build-schemas-check-mode.test.ts, exceeds foreground cap; check:authorable-surface green), post-merge new pin + migrate-sentence + conversions-major18-merge 3/45. lint 3 shards 46/2606, 46/1862, 46 files of which 3 suites failed on a transient missing spec dist during a concurrent gate rebuild, re-run 3/33; post-merge posture+liveness 2/251. plugin-security 185 files 3772 pass 45 skipped; rest meta-object-owd-gate 11/11 (pre and post merge); metadata-core fls-references 52/52; plugin-audit sys-comment-retired-fields 8/8; example-showcase 32/411; dogfood guest-grants + expression-conformance 2/28; cli unit i18n-flow-liveness-gate 8/8, integration validate-lint-mapping-connector-source 3/3, nightly tier validate-json-warning-parity.e2e 3/3. typecheck exit 0: spec (incl. check:test-typecheck compiling the pin's @ts-expect-error), lint, plugin-security, plugin-audit, rest, cli, dogfood, example-showcase. Gates: dispatch-gates --commands (no paths) derived 125 at 1060ed3e7; 124 exit 0, check:platform-checklist exit 1 with 8 findings byte-identical (diff) to the same command on e4ec6ec1a; --ran: 125 accounted, 125 run, 0 NOT-MEASURED. ADR PR #23443 at 785ebd1ee: 19 derived, 19 exit 0, --ran 19 accounted 0 NOT-MEASURED. ESLint proven narrowing at 1060ed3e7: population eslint.config.mjs **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} minus NEVER_LINTED; --no-inline-config --format json over 36 changed lintable files: 36 files, 0 errors, 0 warnings; invariance: no type-aware linting, config and baselines untouched. Ablations via scripts/ablation-replace.mjs, each restored blob==HEAD, git diff HEAD empty, spec-source imports (no dist): (1) tombstone replaced by the old z.enum(...).optional(), anchor 1 to 0: 7 of 18 pin tests red, 11 green; (2) externalSharingModel planted on showcase_announcement, anchor 1 to 0: exactly the tree walk red naming examples/app-showcase/src/data/objects/announcement.object.ts (first attempt was a no-op the tool refused because the replacement contained the anchor; re-run with a landing replacement).",
"mcp_calls": "0",
"api_writes": "5 — relay stroke 1 (run 38537543222): POST /repos/objectstack-ai/objectstack/pulls x2 (#23441, #23443, draft) + POST /repos/objectstack-ai/objectstack/issues/{n}/assignees x2 (baozhoutao); relay stroke 2: POST /repos/objectstack-ai/objectstack/issues/23268/comments (this report). git push of two branches is not REST. Zero label writes: the dispatch named no labels.",
"gates": {
"head": "1060ed3e7",
"derived": 125,
"run": 125,
"exit0": 124,
"red": [
"pnpm check:platform-checklist :: exit 1 (8 pre-existing findings, identical on e4ec6ec1a)"
],
"not_measured": 0,
"adr_pr": {
"head": "785ebd1ee",
"derived": 19,
"exit0": 19
}
},
"line_budget": {
"pr_23441": "+884 / -450 = 1334 changed lines, 52 files",
"pr_23443": "+12 / -0, 1 file"
},
"files_changed": 52,
"deviations": [
"File surface: beyond the claim's list, test files the retirement turned stale were edited — packages/cli/test/{i18n-flow-liveness-gate,validate-json-warning-parity.e2e,validate-lint-mapping-connector-source}.test.ts, packages/lint/src/{lint-liveness-properties,runtime-gate.datasource-writes,runtime-gate.inert-type-writes,validate-security-posture,validate-security-posture.runtime-surface,authoring-rule-input-tier}.test.ts, packages/rest/src/meta-object-owd-gate.test.ts, packages/plugins/plugin-security/src/object-posture-gate.test.ts, packages/plugins/plugin-audit/src/objects/sys-comment-retired-fields.test.ts, packages/qa/dogfood/test/guest-grants-channel.dogfood.test.ts, packages/spec/src/shared/retired-key-removal-version.pin.test.ts, packages/spec/vitest.repo-tests.json; plus packages/lint/src/index.ts, packages/lint/scripts/check-doc-security-posture.mjs, docs/design/permission-model.md and the publicSharing guidance string in object.zod.ts (it steered authors to the retired key). Each listed in the PR body.",
"packages/metadata-core/src/object-schema-fls-references.ts keep table NOT edited: the tombstone keeps the key in the walked shape the table is pinned against.",
"Lint witness kept: several lint/cli tests keep authoring the key on unparsed intakes because a dead ledger row still warns there; the tree-scoped pin excludes those four files by name with the reason.",
"PR #23441 body is stale on one line (dual-build-cjs-loads NOT MEASURED vs exit 0 on the final run); correction text is in summary for the seat.",
"The first pre-push of the ADR commit was refused for a card trailer (Refs line in the commit message); amended locally before any push of that commit."
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: #2776 · item 6 (Portal identity & licensing) gets the pointer ruling item 2 names: external principals reach rows through explicit shares, relationship-based grants (sharing sets) and share links from a separate identity pool, never an external OWD (ADR-0090 D11 as amended in PR #23443) · noted, not filed",
"carrier: PM's objectui follow-up card (ruling item 4) · Studio's object settings select and Ext badge still offer externalSharingModel; until that lands an author who picks a value gets the 422 prescription on save · noted, not filed",
"carrier: whoever next edits skills/objectstack-data/references/lint-rules.md (Tier H, skills/**) · line 23 lists security-external-wider-than-internal, a rule this PR removes; one-row deletion · noted, not filed; dedupe words: lint-rules.md external-wider stale row"
]
}
Shape: draft, base main, line 1 Fixes #23268; +884/−450, 52 files (under 3000); check-governed-merges --pr 23441: not governed; nothing under content/docs/releases/**.
Ruling 6159300530 (A), item by item: 1 — retiredKey() tombstone with the delete-the-key prescription, D2 conversion object-external-sharing-model-removed (census registered, mechanical strip replayed on stored rows), D3 entry, ledger planned → dead, security-external-wider-than-internal and SECURITY_EXTERNAL_WIDER removed (the alias rule keeps its sharingModel half), posture-gate R1's external half removed, both showcase objects, the doc pages, QA item owd-save-gate revision 5, the crossTenantAccess hint; 3 — publicSharing untouched (one mis-nesting guidance string no longer points at the dead key); 5 — the body names os migrate meta --stored for the cloud seat.
Changeset prose, read sentence by sentence against the diff:23268-external-sharing-model-retired.md (specminor + BREAKING, lintminor, plugin-securitypatch; the FROM → TO rows; the prescription quoted verbatim; the retirement kit; the NOT MEASURED out-of-repo line; adr-0087: registered with both ids).
CI on the head (read at this act): all completed checks green; Test Core (3/6) in progress. Enqueue waits on it.
PR body corrected by the seat: the one stale evidence line (check:dual-build-cjs-loads NOT MEASURED) now carries the final-head counts from the dev's report.
Claim amendment (file surface): add the stale tests and fixtures listed under the PR body's file-surface note (packages/cli/test/*, packages/lint/src/*.test.ts, packages/rest/src/meta-object-owd-gate.test.ts, packages/plugins/plugin-security/src/object-posture-gate.test.ts, packages/plugins/plugin-audit/src/objects/sys-comment-retired-fields.test.ts, packages/qa/dogfood/test/guest-grants-channel.dogfood.test.ts, packages/spec/src/shared/retired-key-removal-version.pin.test.ts, packages/spec/vitest.repo-tests.json), packages/lint/src/index.ts, packages/lint/scripts/check-doc-security-posture.mjs, docs/design/permission-model.md. Each one restated or tested the retired key. object-schema-fls-references.ts was claimed but not edited (the tombstone keeps the key in the walked shape).
PR #23443 (Tier H, docs/adr/0090 D11): the D11 amendment says what ruling item 2 binds. A second commit is being added (the skills/objectstack-data/references/lint-rules.md row that names the removed rule; shipped text this card makes false, governed, so it belongs in the Tier H PR). skip-changeset applied by the seat (docs/governed text only, no package). Its contract review and the Tier H route follow on the new head.
Ruling item 2: the D11 amendment says what it binds, clause by clause; the skills/** row deletion is complete (no other skills/** line names the key or rule).
Merged through the merge queue; the squash equals the reviewed head 1060ed3e7 by git patch-id --stable (410a9ae4… on both sides); 301eec6df is an ancestor of origin/main.
The card closed completed on Fixes #23268; pm:dispatched is removed in this act.
Session session_01D6F9axfoPzR7t8G1J7Ytzz · baozhoutao. os-zhuang approved on head 1e1491293 (an authorized approver, GOVERNED_APPROVERS) and queued it; the squash equals that head by git patch-id --stable; on origin/main. needs-user-decision was removed from the PR. The #2776 item 6 pointer (ruling item 2) is posted. Everything ruling 6159300530 binds is now landed, except objectstack-ai/objectui#12353 (objectui's lane).
Activity
objectstack-fleet commentedon Oct 13, 2026
Triage: first grade,
bug·security·priority:p2·target:v18·domain:spec·area:access,pm:blockedon #23266 (findingremoved).externalSharingModelis enforced or removed (ADR-0049), after the guest row scope is ruledTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-13T14:10Z. ⛔ Not a claim, ⛔ not a dispatch.origin/main(c0569797a6): outsidepackages/spec,externalSharingModelappears in non-test source only as a comment inpackages/plugins/plugin-audit/src/objects/sys-comment.object.ts:34. There is no runtime reader. The positive control findssharingModelacrossplugin-security's source. No runtime principal kind is external today, so the only principal the key could govern is the guest.security: a security-looking key that changes no answer (ADR-0049). It leaks nothing today, because no deployment can bind a set to the guest (security: no door can bind a permission set to theguestanchor — ADR-0138 D3 item 5 has no enforcer (PUT /meta/position/guestanswers 403 NOT_OVERRIDABLE; guest suggestions are refused) #23267). It becomes live once that door opens.target:v18: if the answer is removal, that is a breaking narrowing of the authoring surface and should ride the major.packages/speccontract (ADR-0090 D11), sodomain:specdecides enforce-or-remove. Removal narrows, soClause-②: no, with contract-tier review.guestanchor's bindings (D3, D4) #23224,domain:engine). This card then closes on that landing, if nothing else is external.showcase_announcementdrops it.packages/spec(the key) and, under B only, the row pipeline through E1 (domain:spec); rationale: a declared security key with no reader.objectstack-fleet commentedon Oct 13, 2026
Unlock scan:
pm:blocked→pm:queue. #23266 is ruled C, so the guest never readsexternalSharingModel. What remains is ADR-0049 enforce-or-remove for external principals, which the spec seat decidesTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-13T15:00Z. ⛔ Not a claim, ⛔ not a dispatch.6148183570): the guest takes neithersharingModelnorexternalSharingModel. ADR-0090 D9 and D11 are unchanged, so D11 still declares the key as the OWD for external principals (a portal or partner identity pool).mainis external (re-checked last round), so the key has nothing it could govern. It is still a security-looking key that an author can set and that changes no answer.showcase_announcementdeclaresexternalSharingModel: private.[Decision]card with the workload of each option. ⛔ This seat does not choose it.securitytarget:v18, kept. A removal is a breaking narrowing and should ride the major.domain:spec, kept.objectstack-fleet commentedon Oct 13, 2026
决策分析:
externalSharingModel退役、保留还是实现 ·domain:spec席 3(席位帖 #18883)·session_012uFegdfR6K6uqvLzt7XsdZ· 2026-10-13T19:56Z分诊的放行(
6148526733)写明:凡要改已接受记录原文的路线,一律交维护者裁定,⛔ 本席不选。本评论是本席的实测和推荐,裁定在维护者手里。读数取自origin/main4d4040ccb,objectui 取自72594f29e。一句话问题
对象设计器里有一个"外部用户能看到哪些记录"的安全开关,作者会设置它,Studio 也展示它,可它对任何人的访问结果都没有影响。产品里根本没有外部用户,而平台另外两份已实施的授权记录又规定,外部用户将来也不看这个开关。
Governing text
docs/adr/0090-permission-model-v2-concept-convergence.md:460-479):"sharingModelstays scalar and internal; an optionalexternalSharingModelis added with the same canonical enum, defaultprivate." 以及 "external visibility = own records + explicit shares + external OWD"。docs/adr/0095-authz-kernel-tenant-layer-and-posture-ladder.md:30-31,:186,状态为已接受、已实施):"EXTERNALrung's semantics are defined and test-locked now (explicit shares only, never OWD)";EXTERNAL = 0, // explicitly shared rows only — never OWD-derived visibility。ADR-0099 在其上补了 EXTERNAL 档的分级。5727134555,[Decision] Route declared≠enforced work by the SEAM, not the layer — aSeam:line on filing, vertical dispatch by default in the spec lane, automatic parent + sub-issues for spec↔objectui seams, Journey as a filter, bulk retirement per spec family, Console Pin Gate back to required (the maintainer's 「同意」 on the five-line batch, 2026-09-18) #18900):「主流平台有没有这个能力 … 有 ⇒ 补消费端;没有 ⇒ 退役」。两份已接受记录在这里互相冲突: D11 说外部可见性包含外部 OWD,ADR-0095 说 EXTERNAL 档从不看 OWD。无论选哪条路,都得一并消掉这个冲突。
协议声明,以及是否改协议
packages/spec/src/data/object.zod.ts:2776:封闭的四值枚举,.optional(),没有.default(),所以 "defaults to private" 只写在说明文字里。前提(各附 re-check 命令)
metadata-core的保留表,以及两处注释。'packages/*/src/**/*.ts',漏掉了直接位于src/下的文件。结论不变:仍然没有运行时读者,只是少数了两处创作期读者。packages/core/src/security/assemble-execution-context.ts:358恒为audience: undefined(注释:没有任何传输层解析出外部受众)。PortalSchema已在 PR feat(spec)!: remove the dead PortalSchema — never-enforced no-op projection (#3464) #3486 裁掉(+49/−976)。planned,但它的依据已经失效。packages/spec/liveness/object.json:240-245写着 "tracked on ADR-0090: Permission Model v2 — concept convergence, final naming, AI-authoring safety (tracking) #2696",而 ADR-0090: Permission Model v2 — concept convergence, final naming, AI-authoring safety (tracking) #2696 已于 2026-07-10 以 completed 关闭。packages/lint/src/lint-liveness-properties.ts:258),可实际上没有人在做这个消费端。security-external-wider-than-internal(packages/lint/src/validate-security-posture.ts:584-597)在运行时发布门答 422。具体问题
保留一个对任何主体都不生效的安全开关,与 ADR-0049 和"声明即强制"冲突。可是退役它,要改 ADR-0090 D11 的原文。而 D11 本身已经和已实施的 ADR-0095 相矛盾。
选项 × 真实代价 × 工作量
account.object.ts:26、announcement.object.ts:34);文档 5 页;QA 项access-security.owd-save-gate的外部条款。crossTenantAccess的退役提示(object.zod.ts:603-607)不再指向它。objectui 的 Studio 下拉与 Ext 徽标(6 个源文件、9 个测试)另开卡跟进。D11 改文走 Tier H PR。domain:spec:objectstack 1 个 PR(契约评审)、objectui 1 张跟进卡、1 个 Tier H 改文 PR。先例:#19618tenancy.organizationField(+688/−287,22 个文件);#22990 邮件模板category/isSystem(+1096/−187,40 个文件,含示例与文档清扫);#12619allowRestore/allowPurge(+693/−135)。objectui 一侧没有测过先例。planned(现状)protocol.ts:5947-5948里 R2 仍有效的过时描述删掉;Studio 加一句"尚未生效"(objectui 卡)。domain:spec:1 个小 PR,加 1 张 objectui 小卡。先例:PR #2800 "honestly marked"(+194/−7,12 个文件)。键会一直挂着、一直要维护。audience: 'external'的解析),再在行级求值里加外部分支,同时裁掉 D11 与 ADR-0095/0099 的冲突(二者只能留一个)。业务含义直译
anon策略同类。四轴论证(从业务立场)
PortalSchema也已裁掉;设置这个键的只有示例应用app-showcase的两个对象,另外只有 QA 清单在测它。零拉动。private,就相信门户用户被挡住了,而没有任何错误会纠正它(警告不是拒绝)。等门户按 ADR-0099 的规则上线,这个开关照样不生效,误信会延续下去。在 A 下,作者在发布时被响亮拒绝,并拿到处方;在 B 下,作者只收到一条警告。推荐、回退与置信缺口
只看项目长远合理性选 A;另三轴是否翻转:否(业务拉动为零、防 AI 犯错、不扩散,三轴同向支持 A)。
裁后执行
externalSharingModelis declared and authored but has no runtime reader — an object'sexternalSharingModel: privatechanges no answer for any principal (ADR-0090 D11 declared-not-enforced) #23268,派一个 objectstack PR:删除键,加 ADR-0087 退役条目(按普查定为registered带机械剥除,或not-required),清理 lint 规则、posture gate 外部分支、账本行、示例、文档和 QA 项,并改写crossTenantAccess的退役提示。契约评审后进队列。externalSharingModel退役"。Permission Model v2 — named follow-up ADRs (post-ADR-0090 parking lot) #2776 第 6 项记一笔指针。protocol.ts过时注释),外加一张 objectui 卡("尚未生效"提示)。D11 与 ADR-0095 的冲突记到 Permission Model v2 — named follow-up ADRs (post-ADR-0090 parking lot) #2776,挂"待裁"。security:externalSharingModelis declared and authored but has no runtime reader — an object'sexternalSharingModel: privatechanges no answer for any principal (ADR-0090 D11 declared-not-enforced) #23268 关闭为"保留、已如实标注"。os-decision-facets
Prior rulings read: externalSharingModel, external, portal, partner, audience → 30 hits; ADR-0090 D11, ADR-0090 D10, ADR-0090 D9, ADR-0095 D2, ADR-0099 D2, ADR-0099 D3; thread: none
6148183570([Decision] 访客(guest)的行可见范围:沿用对象的内部共享模型(A,现状,公开对象全部行可见)、按 externalSharingModel 判(B)、还是只经分享链接(C) #23266,C 「同意」:访客两个开关都不看,D9/D11 不变);5995552118([finding] three "declared, not enforced yet" rows in the metadata-form reconciliation ledger are stale:field.picklist,action.onSuccessandaction.outcomeMessagesreadlive, and none has had its offer decided #21863,「同意」:这个键的表单账本行保持planned);5287702149([3 of #7891] Flip the registration:runtimeTypesgainsobject+permission/book— whole rule families cross the publish door #8310:R2 退役);5164250665(sys_comment.visibilityandsys_comment.reply_countare declared but nothing anywhere reads or maintains them (ADR-0049 enforce-or-remove) #4756:sys_comment.visibility在 D11 落地前移除);5727134555([Decision] Route declared≠enforced work by the SEAM, not the layer — aSeam:line on filing, vertical dispatch by default in the spec lane, automatic parent + sub-issues for spec↔objectui seams, Journey as a filter, bulk retirement per spec family, Console Pin Gate back to required (the maintainer's 「同意」 on the five-line batch, 2026-09-18) #18900:主流平台判据)。相关单与 PR
#23266(裁决 C)· #2776(搁置的后续)· #2696(已关闭,账本仍挂着它)· #1294(门户,not_planned)· PR #2697(引入,v13)· PR #2800(如实标注,v14.4.0)· PR #3052 / PR #7710(发布门)· PR #3486(
PortalSchema裁掉)· #23267(访客绑定门)。Generated by Claude Code
4 remaining items
objectstack-fleet commentedon Oct 14, 2026
Claim: PM loop round 1
Session:
session_01D6F9axfoPzR7t8G1J7YtzzAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-23268-retire-external-sharing-modelWorktree:
objectstack-issue-23268Domain:
domain:specSeat:
domain:spec#3(seat post #18883)File surface (at
origin/maine4ec6ec1aor later; stop on a breach and explain it in the report):packages/spec/src/data/object.zod.ts(the key) and its tests;packages/spec/liveness/object.json(the row);packages/spec/src/migrations/entries/**(one ADR-0087 retirement entry, class by census) and the registries they feed;packages/spec/src/kernel/execution-context.zod.ts(one mention);packages/spec/src/ai/solution-blueprint*;packages/lint/src/validate-security-posture.ts,packages/lint/src/rule-explanations.ts,packages/lint/src/authoring-rules.ts(the alias rule andsecurity-external-wider-than-internal);packages/plugins/plugin-security/src/object-posture-gate.ts(the external branch);packages/metadata-core/src/object-schema-fls-references.ts(keep table); comment lines inpackages/metadata-protocol/src/protocol.ts,packages/plugins/plugin-audit/src/objects/sys-comment.object.ts,packages/plugins/plugin-sharing/src/sharing-service.ts;examples/app-showcase/src/**(the two objects, security index/positions, tour doc, coverage);content/docs/permissions/**,content/docs/protocol/objectql/security.mdxand the regeneratedcontent/docs/references/**;docs/qa/platform-checklist/areas/access-security.json(the external clause);.changeset/23268-*.md. Separately, a second Tier H PR touching onlydocs/adr/0090-*.md(the D11 sentence). ⛔ Notcontent/docs/releases/**.Container & model:
M,mode:subagent,model: default (per dispatch-gates --tier: no path-derived mandate; floor · default · ceiling); review before the queue at CONTRACT_REVIEW_TIER (a narrowing: "a card that only NARROWS one is Clause-②: no … and owes one contract-review-tier review before the queue")Clause-②: no
Responsibility:
packages/specdeclares the key (ADR-0090 D11) and no row-pipeline code reads it | ADR-0095/0099's EXTERNAL rung (explicitly shared rows only) already covers external visibility, and no runtime principal is external (assemble-execution-context.ts:358audience: undefined) | object authors reach it; today only examples/app-showcase sets itThread-read: 6159300530
Serial constraints cleared:
packages/metadata-protocol/src/protocol.tsis in the claimed surfaces of #23297, #23225, #23357 (seat 1, in flight) — this card touches comment lines only;packages/spec/src/kernel/execution-context.zod.tsis in #23287's (seat 2, in flight);examples/app-showcaseis in #15204's (epic #15194, in flight); open PR #23368 (plugin-security) shares none of these files. None is aSINGLE_CLAIM_PATHSpath; ordinary concurrency, the later lander mergesmain.Ruling of record:
6159300530(letter A, the maintainer's 「23268 同意」); premises re-read onorigin/maine4ec6ec1aat 2026-10-14T05:28Z: no row-pipeline reader of the key outsidepackages/spec,audience: undefinedatassemble-execution-context.ts:358, no open PR names the key.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Regrade: p2 → p1 ·
target:v18· protocol-first rule, all lanes · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-14T08:23ZUnder the maintainer's grading rule, recorded on #6015 (
6163099154, amended6163539661) and extended today, 「立卡,规则扩到所有车道,REST 收窄也算」: every metadata-protocol change, and every narrowing of a REST or data door, is p1 +target:v18in every lane. The skill text follows in #23431.externalSharingModelfrom the object contract (packages/spec/src/data/object.zod.ts). That is an authoring-surface narrowing, and it rides the 18 major.Lane, scope and Clause ② are unchanged.
objectstack-fleet commentedon Oct 14, 2026
os-dev-report
{ "issue": 23268, "status": "done", "branch": "claude/issue-23268-retire-external-sharing-model", "pr": "https://github.com/objectstack-ai/objectstack/pull/23441", "session": "session_01D6F9axfoPzR7t8G1J7Ytzz", "premise_still_valid": true, "summary": "Retired object.externalSharingModel per ruling 6159300530 letter A: retiredKey() tombstone on ObjectSchema with the delete-the-key prescription, D2 conversion object-external-sharing-model-removed (step 18, order 64, retiredFromLoadPath, retiredAfter 17.7.0), RETIRED_KEYS_BY_MAJOR[18] data/Object:externalSharingModel, D3 object-external-sharing-model-retired plus its STEP18_RATIONALE fragment; ledger row planned to dead (tombstone row kept), lint rule security-external-wider-than-internal and its SECURITY_EXTERNAL_WIDER export removed, posture gate R1 external half removed, both showcase objects, five doc pages plus docs/design, the QA item owd-save-gate (revision 5) and the crossTenantAccess hint no longer name the key; new tree-scoped retirement pin. Census class: registered (Studio offered the select and PUT /meta/object persisted it, so stored object rows can carry it; the replayed strip heals them). Companion Tier H PR #23443 (docs/adr/0090 only, Refs) carries the D11 amendment with the 维护者速读 section. Correction for the seat to apply to PR #23441's body (dev writes the body once): the Evidence bullet's gate line should read 'at 1060ed3e7: 125 derived, 125 run, 124 exit 0; check:platform-checklist exit 1 with the 8 pre-existing findings identical to e4ec6ec1a; --ran 125 accounted, 0 NOT-MEASURED' — the body still says check:dual-build-cjs-loads NOT MEASURED, but it exited 0 on the final-head run.", "tests": "All at head 1060ed3e7 unless noted (merged origin/main 04a314873 via os-regen-merge.sh). spec: build + check:generated all 14 up to date; local project 3 shards pre-merge 225/7366, 224/6477(1 todo), 224/6619 + 1 red fixed (retired-key-removal-version JOIN) re-run 1/20; post-merge conversions+migrations+object.test+that pin 20/956; repo project 2 shards 30/470 + 29/471 (NOT MEASURED: scripts/build-schemas-check-mode.test.ts, exceeds foreground cap; check:authorable-surface green), post-merge new pin + migrate-sentence + conversions-major18-merge 3/45. lint 3 shards 46/2606, 46/1862, 46 files of which 3 suites failed on a transient missing spec dist during a concurrent gate rebuild, re-run 3/33; post-merge posture+liveness 2/251. plugin-security 185 files 3772 pass 45 skipped; rest meta-object-owd-gate 11/11 (pre and post merge); metadata-core fls-references 52/52; plugin-audit sys-comment-retired-fields 8/8; example-showcase 32/411; dogfood guest-grants + expression-conformance 2/28; cli unit i18n-flow-liveness-gate 8/8, integration validate-lint-mapping-connector-source 3/3, nightly tier validate-json-warning-parity.e2e 3/3. typecheck exit 0: spec (incl. check:test-typecheck compiling the pin's @ts-expect-error), lint, plugin-security, plugin-audit, rest, cli, dogfood, example-showcase. Gates: dispatch-gates --commands (no paths) derived 125 at 1060ed3e7; 124 exit 0, check:platform-checklist exit 1 with 8 findings byte-identical (diff) to the same command on e4ec6ec1a; --ran: 125 accounted, 125 run, 0 NOT-MEASURED. ADR PR #23443 at 785ebd1ee: 19 derived, 19 exit 0, --ran 19 accounted 0 NOT-MEASURED. ESLint proven narrowing at 1060ed3e7: population eslint.config.mjs **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} minus NEVER_LINTED; --no-inline-config --format json over 36 changed lintable files: 36 files, 0 errors, 0 warnings; invariance: no type-aware linting, config and baselines untouched. Ablations via scripts/ablation-replace.mjs, each restored blob==HEAD, git diff HEAD empty, spec-source imports (no dist): (1) tombstone replaced by the old z.enum(...).optional(), anchor 1 to 0: 7 of 18 pin tests red, 11 green; (2) externalSharingModel planted on showcase_announcement, anchor 1 to 0: exactly the tree walk red naming examples/app-showcase/src/data/objects/announcement.object.ts (first attempt was a no-op the tool refused because the replacement contained the anchor; re-run with a landing replacement).", "mcp_calls": "0", "api_writes": "5 — relay stroke 1 (run 38537543222): POST /repos/objectstack-ai/objectstack/pulls x2 (#23441, #23443, draft) + POST /repos/objectstack-ai/objectstack/issues/{n}/assignees x2 (baozhoutao); relay stroke 2: POST /repos/objectstack-ai/objectstack/issues/23268/comments (this report). git push of two branches is not REST. Zero label writes: the dispatch named no labels.", "gates": { "head": "1060ed3e7", "derived": 125, "run": 125, "exit0": 124, "red": [ "pnpm check:platform-checklist :: exit 1 (8 pre-existing findings, identical on e4ec6ec1a)" ], "not_measured": 0, "adr_pr": { "head": "785ebd1ee", "derived": 19, "exit0": 19 } }, "line_budget": { "pr_23441": "+884 / -450 = 1334 changed lines, 52 files", "pr_23443": "+12 / -0, 1 file" }, "files_changed": 52, "deviations": [ "File surface: beyond the claim's list, test files the retirement turned stale were edited — packages/cli/test/{i18n-flow-liveness-gate,validate-json-warning-parity.e2e,validate-lint-mapping-connector-source}.test.ts, packages/lint/src/{lint-liveness-properties,runtime-gate.datasource-writes,runtime-gate.inert-type-writes,validate-security-posture,validate-security-posture.runtime-surface,authoring-rule-input-tier}.test.ts, packages/rest/src/meta-object-owd-gate.test.ts, packages/plugins/plugin-security/src/object-posture-gate.test.ts, packages/plugins/plugin-audit/src/objects/sys-comment-retired-fields.test.ts, packages/qa/dogfood/test/guest-grants-channel.dogfood.test.ts, packages/spec/src/shared/retired-key-removal-version.pin.test.ts, packages/spec/vitest.repo-tests.json; plus packages/lint/src/index.ts, packages/lint/scripts/check-doc-security-posture.mjs, docs/design/permission-model.md and the publicSharing guidance string in object.zod.ts (it steered authors to the retired key). Each listed in the PR body.", "packages/metadata-core/src/object-schema-fls-references.ts keep table NOT edited: the tombstone keeps the key in the walked shape the table is pinned against.", "Lint witness kept: several lint/cli tests keep authoring the key on unparsed intakes because a dead ledger row still warns there; the tree-scoped pin excludes those four files by name with the reason.", "PR #23441 body is stale on one line (dual-build-cjs-loads NOT MEASURED vs exit 0 on the final run); correction text is in summary for the seat.", "The first pre-push of the ADR commit was refused for a card trailer (Refs line in the commit message); amended locally before any push of that commit." ], "open_questions": [], "out_of_scope_findings": [ "carrier: #2776 · item 6 (Portal identity & licensing) gets the pointer ruling item 2 names: external principals reach rows through explicit shares, relationship-based grants (sharing sets) and share links from a separate identity pool, never an external OWD (ADR-0090 D11 as amended in PR #23443) · noted, not filed", "carrier: PM's objectui follow-up card (ruling item 4) · Studio's object settings select and Ext badge still offer externalSharingModel; until that lands an author who picks a value gets the 422 prescription on save · noted, not filed", "carrier: whoever next edits skills/objectstack-data/references/lint-rules.md (Tier H, skills/**) · line 23 lists security-external-wider-than-internal, a rule this PR removes; one-row deletion · noted, not filed; dedupe words: lint-rules.md external-wider stale row" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Review: ACCEPT (PR #23441) · PR #23443 pending its new head ·
domain:specseat 3 · 2026-10-14T09:20ZSession
session_01D6F9axfoPzR7t8G1J7Ytzz·baozhoutao. Reviewer of record; checked against GitHub, not the report.PR #23441 @
1060ed3e74306c46a60e51745479a418806475bf— ACCEPT.main, line 1Fixes #23268; +884/−450, 52 files (under 3000);check-governed-merges --pr 23441: not governed; nothing undercontent/docs/releases/**.6159300530(A), item by item: 1 —retiredKey()tombstone with the delete-the-key prescription, D2 conversionobject-external-sharing-model-removed(censusregistered, mechanical strip replayed on stored rows), D3 entry, ledgerplanned→dead,security-external-wider-than-internalandSECURITY_EXTERNAL_WIDERremoved (the alias rule keeps itssharingModelhalf), posture-gate R1's external half removed, both showcase objects, the doc pages, QA itemowd-save-gaterevision 5, thecrossTenantAccesshint; 3 —publicSharinguntouched (one mis-nesting guidance string no longer points at the dead key); 5 — the body namesos migrate meta --storedfor the cloud seat.23268-external-sharing-model-retired.md(specminor+ BREAKING,lintminor,plugin-securitypatch; the FROM → TO rows; the prescription quoted verbatim; the retirement kit; the NOT MEASURED out-of-repo line;adr-0087: registeredwith both ids).CONTRACT_REVIEW_TIER, record posted on PR feat(spec)!: retire the object's externalSharingModel — the external org-wide default no principal ever read (#23268) #23441 on this head.Test Core (3/6)in progress. Enqueue waits on it.check:dual-build-cjs-loadsNOT MEASURED) now carries the final-head counts from the dev's report.packages/cli/test/*,packages/lint/src/*.test.ts,packages/rest/src/meta-object-owd-gate.test.ts,packages/plugins/plugin-security/src/object-posture-gate.test.ts,packages/plugins/plugin-audit/src/objects/sys-comment-retired-fields.test.ts,packages/qa/dogfood/test/guest-grants-channel.dogfood.test.ts,packages/spec/src/shared/retired-key-removal-version.pin.test.ts,packages/spec/vitest.repo-tests.json),packages/lint/src/index.ts,packages/lint/scripts/check-doc-security-posture.mjs,docs/design/permission-model.md. Each one restated or tested the retired key.object-schema-fls-references.tswas claimed but not edited (the tombstone keeps the key in the walked shape).PR #23443 (Tier H,
docs/adr/0090D11): the D11 amendment says what ruling item 2 binds. A second commit is being added (theskills/objectstack-data/references/lint-rules.mdrow that names the removed rule; shipped text this card makes false, governed, so it belongs in the Tier H PR).skip-changesetapplied by the seat (docs/governed text only, no package). Its contract review and the Tier H route follow on the new head.Out-of-scope findings:
externalSharingModeldropdown and the Ext badge once @objectstack/spec retires the key (objectstack#23268, ruling A) objectui#12353 (filed under ruling item 4).skills/**lint-rules row → PR docs(adr): ADR-0090 D11 — external visibility is the EXTERNAL rung; externalSharingModel retired (#23268) #23443 (above).Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Review: PR #23443 @
1e1491293dd46fd3657ad481bf120ddce9e7cd92— reviewed, Tier H, waiting for an authorized approval ·domain:specseat 3 · 2026-10-14T09:53ZSession
session_01D6F9axfoPzR7t8G1J7Ytzz·baozhoutao.Refs #23268(no closing keyword; PR feat(spec)!: retire the object's externalSharingModel — the external org-wide default no principal ever read (#23268) #23441 closes the card),Clause-②: no; 3 files:docs/adr/0090-…md(+12),skills/objectstack-data/references/lint-rules.md(−1),.changeset/23268-skills-lint-rules-row.md(@objectstack/skillspatch). Governed (Tier H):docs/adr/**andskills/**.skills/**row deletion is complete (no otherskills/**line names the key or rule).CONTRACT_REVIEW_TIERon this head, superseding the FAIL once509e82c. That FAIL was the seat's error: it had labelled the PRskip-changeset, butskills/**ships in@objectstack/skills. The label is removed and the changeset added. The gate that let the label pass is filed as finding(gates):check-empty-changesetandCheck Changesetroute 2 still say askills/**-only PR releases nothing, so it passes with no changeset; since #22658@objectstack/skillspublishesskills/**#23451.needs-user-decisionis on the PR; review requested fromos-zhuangandhotlong. The seat lands it after an authorized approval, preferably after PR feat(spec)!: retire the object's externalSharingModel — the external org-wide default no principal ever read (#23268) #23441 merges.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Landed · PR #23441 →
main301eec6df·domain:specseat 3 · 2026-10-14T09:59ZSession
session_01D6F9axfoPzR7t8G1J7Ytzz·baozhoutao.1060ed3e7bygit patch-id --stable(410a9ae4…on both sides);301eec6dfis an ancestor oforigin/main.completedonFixes #23268;pm:dispatchedis removed in this act.Refs #23268: the ADR-0090 D11 amendment and theskills/**row), contract PASS on1e1491293, review requested fromos-zhuangandhotlong. This seat follows it to MERGED and then posts the Permission Model v2 — named follow-up ADRs (post-ADR-0090 parking lot) #2776 item 6 pointer (ruling item 2).externalSharingModeldropdown and the Ext badge once @objectstack/spec retires the key (objectstack#23268, ruling A) objectui#12353 (the Studio half, ruling item 4) is in objectui's queue. objectui meets the refusal when it moves to a@objectstack/specrelease carrying301eec6df, so Studio's select should go no later than that bump. (Corrected in place: an earlier wording said the pin already carries it.)Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Landed · PR #23443 →
main11d4c5a6d(Tier H, approved byos-zhuang) ·domain:specseat 3 · 2026-10-14T10:26ZSession
session_01D6F9axfoPzR7t8G1J7Ytzz·baozhoutao.os-zhuangapproved on head1e1491293(an authorized approver,GOVERNED_APPROVERS) and queued it; the squash equals that head bygit patch-id --stable; onorigin/main.needs-user-decisionwas removed from the PR. The #2776 item 6 pointer (ruling item 2) is posted. Everything ruling6159300530binds is now landed, except objectstack-ai/objectui#12353 (objectui's lane).Generated by Claude Code