Repository navigation
Description
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Filing gate: ① consistency defect owed by landed work (#27425, PR #27476 f57eddcf), from the post-landing contract review 6493803142 (④ should-fix 1) and the seat carry 6493297056.
Reader: the epic PM (#27261) dispatches it in the epic subtree; the fix is in packages/spec/src/data/injected-system-column-provenance.ts, packages/plugins/plugin-sharing/src/federated-phantom-anchors.ts and packages/lint/src/validate-external-owner-sharing.ts.
Dedupe (open AND closed): "resolveInjectedColumnProvenance columnMap owner_id plugin-sharing convergence" → 2 hits, none this defect (#8119 is the closed #7858 write-gate twin).
reach: no runtime exposure; both sides fail closed. Read on main: for a federated object that maps a remote column onto owner_id through external.columnMap without declaring it, resolveInjectedColumnProvenance(schema, 'owner_id') answers injected-unprovisioned, while plugin-sharing's hasPhantomOwnerAnchor answers false (mapsRemoteColumnOntoOwner) and scopes over the mapped column. The spec module's convergence map still names #7858 as plugin-sharing converging on the helper.
type: Bug
维护者速读
- 现象: 「外部对象的 owner 是否可证」有三处判定:spec 的
resolveInjectedColumnProvenance、plugin-sharing 的hasPhantomOwnerAnchor、lint 的hasUnprovableOwnerAnchor/columnMapTargets。sharing: aprivateexternal object whose driver servesowner_idwithout declaring it infieldsgives every member every row —hasPhantomOwnerAnchorreadsfieldsonly, so the read filter goes unscoped (epic #27261, found by #27332's dev) #27425 之后 plugin-sharing 把columnMap映到owner_id算作已证,spec 的 helper 不认,收敛表也没记这个例外。 - 原因: sharing: a
privateexternal object whose driver servesowner_idwithout declaring it infieldsgives every member every row —hasPhantomOwnerAnchorreadsfieldsonly, so the read filter goes unscoped (epic #27261, found by #27332's dev) #27425 在 plugin-sharing 里就地加了columnMap分支,没回写到 spec 的唯一来源。 - 影响: 目前两边都 fail closed,不泄露;但下一个按收敛表对齐 helper 的消费方会悄悄放宽或收窄这类对象。
- 最小修法: 让 helper 认
columnMap分支(或在收敛表里写明例外),plugin-sharing 与 lint 改读 helper,删掉重复的columnMap读取。 - 需要您做的: 无。
Task
- One reader decides "owner anchor provable from declared metadata" (a declared
owner_idfield, or anexternal.columnMapvalueowner_id). EitherresolveInjectedColumnProvenancetakes thecolumnMaparm, or the spec module's convergence map records it as a named exception; plugin-sharing (mapsRemoteColumnOntoOwner) and lint (columnMapTargets,hasUnprovableOwnerAnchor) read that one source. - No verdict moves: plugin-sharing's read/write scopes, the lint rules' findings and hints stay byte-identical on the existing pins (federated phantom-scoping suites,
external-owner-anchor-sql-read.test.ts,validate-external-owner-sharing.test.ts). - The convergence row for plugin-sharing lowers
__readScopeown/unit to anowner_idpredicate on federated objects, whereowner_idis a phantom column #7858 ininjected-system-column-provenance.tsreads the head's behaviour.
Clause-②: decided by the dev from the diff (a new exported helper arm in @objectstack/spec is a public-surface change) · Metadata-protocol: none. Touches packages/spec/src/**: owes a full Fable review and a CONTRACT_REVIEW_TIER record before landing.
Activity
objectstack-fleet commentedon Oct 29, 2026
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Claim: PM loop round 7 2026-10-29T21:57Z
Session:
session_017ihL2c5z3q4h8inrn5kwqV(epic PM); implementing card threadsession_017ihL2eCEdjSZBk7DGrPPFgAccount:
marchtianBranch:
claude/project-thread-c3dfhl(the card thread's platform-assigned branch — measured platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-27661Domain:
domain:specSeat:
epic:#27261#1(epic PM; nodomain:*seat). Maintainer order, 田騏 2026-10-29T21:50Z: 「给我解释后续还有哪些卡,以及为什么不派」 (epic subtree p3 cards are dispatched, not left by pull)File surface:
packages/spec/src/data/injected-system-column-provenance.ts(one exported owner-anchor reader plus itscolumnMapreader; the #7858 convergence row),packages/plugins/plugin-sharing/src/federated-phantom-anchors.ts(hasPhantomOwnerAnchorreads the spec pair),packages/lint/src/validate-external-owner-sharing.ts(hasUnprovableOwnerAnchor/columnMapTargetsread the spec pair), their tests, generated references the spec change re-records,.changeset/**(stop on breach; explain in the report)Container & model:
S,mode:thread,model: default tier (dispatch-gates --tier: no path-derived mandate),effort: highClause-②: yes
Metadata-protocol: none
Responsibility: #27425 (PR #27476) added the
columnMap→owner_idarm in plugin-sharing only; review 6493803142 ④ should-fix 1 and seat carry 6493297056Thread-read: none
Premise: holds — on
origin/mainresolveInjectedColumnProvenancehas nocolumnMaparm while plugin-sharing'smapsRemoteColumnOntoOwnerand lint'scolumnMapTargetseach read it.Serial constraints: #27662 (card thread) edits plugin-sharing's write-deny diagnostic, not
federated-phantom-anchors.ts's predicate; say in the report if the two overlap. Contract face (published spec +Clause-②: yes): the PR body says "Fable review owed", and it does not land until aCONTRACT_REVIEW_TIERrecord PASS is on its head.objectstack-fleet commentedon Oct 29, 2026
epic:#27261#1 · marchtian · session_017ihL2eCEdjSZBk7DGrPPFg
os-dev-report
{ "issue": 27661, "status": "done", "branch": "claude/project-thread-c3dfhl", "pr": "https://github.com/objectstack-ai/objectstack/pull/27794", "session": "session_017ihL2eCEdjSZBk7DGrPPFg", "premise_still_valid": true, "summary": "Premise held on origin/main 481ade61 (and 3702be40): resolveInjectedColumnProvenance has no columnMap arm, plugin-sharing hasPhantomOwnerAnchor restated the identity comparison plus mapsRemoteColumnOntoOwner, lint hasUnprovableOwnerAnchor restated columnMapTargets, and the #7858 convergence row named the bare provenance verdict. @objectstack/spec/data now exports isOwnerAnchorUnprovable(def) (provenance(owner_id) === injected-unprovisioned AND no external.columnMap value owner_id) and externalColumnMapTargets(def, field); plugin-sharing's hasPhantomOwnerAnchor and lint's hasUnprovableOwnerAnchor return it, lint's externalOwnershipGap reads externalColumnMapTargets, and the four private copies in federated-phantom-anchors.ts are deleted. resolveInjectedColumnProvenance is unchanged by design (PM-accepted plan): lint's security-external-owner-unmapped needs mapped-but-undeclared to stay distinct. Two unpinned edge moves (hand-built doc with no owner_id key; byte-identical declared owner_id on a tier that withholds it) are named in the PR's Acceptance notes, both closed. Head e402956d = fe45a398 + merge of origin/main 79d78b4e via os-regen-merge.sh (two earlier wip commits on the branch; the queue squashes).", "tests": "Final head e402956d, heavy runs under scripts/pm/os-verify-lock.sh. spec full test (on fe45a398, spec/src/data unchanged by the merge): Test Files 754 passed (754), Tests 22410 passed | 1 todo; plugin-sharing vitest run: 58/58 files, 1290/1290 tests; lint vitest run: 178/178 files, 7623/7623 tests (validate-external-owner-sharing.test.ts, external-owner-anchor-sql-read.test.ts and the federated phantom-scoping suites unchanged and green, so verdicts, findings and hints keep their bytes); metadata-core test 21/21 files, 873/873; spec/lint/plugin-sharing typecheck exit 0. New: 4 cases in packages/spec/src/data/injected-system-column-provenance.test.ts (10/10). Ablation on the committed head: deleting the columnMap guard in isOwnerAnchorUnprovable (grep -c 0 mutated, 1 restored) + spec rebuild turned plugin-sharing sql-read + phantom-scoping 3 failed of 55 and lint owner-sharing + security-posture 1 failed of 202; restored via git checkout HEAD, git diff HEAD empty, rebuilt: 55/55 and 202/202.", "gates": "Local subset on e402956d after a full pnpm build (lock VERDICT exit 0): dispatch-gates --commands derived 86 families (86 before and after the merge), every one run with its exit code recorded before any pipe: 86 exit 0 (spec check:api-surface unchanged-surface line, check:export-origins 5651 exports, check:docs 232 in sync, check:generated 14/14 up to date, check:dual-build-cjs-loads, check:lean-entry-closure, check:i18n, check:type-check-debt, check-adr-0087-registration / check-empty-changeset / check-changeset-no-major --base origin/main, the self-tests). dispatch-gates --ran: 86 derived, 86 run, 0 NOT-MEASURED, 0 UNRUN. Before the merge check:generated found api-surface/ and export-origins/ stale (the two new names), regenerated and committed. PR check-runs: in_progress at report time (PR opened 23:27Z); not waited on, per the role file.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "line_budget": "n/a — the claim sets none; three-dot diff vs origin/main: 8 files, +176/-116", "files_changed": [ ".changeset/27661-owner-anchor-one-reader.md", "packages/lint/src/validate-external-owner-sharing.ts", "packages/plugins/plugin-sharing/src/federated-phantom-anchors.ts", "packages/plugins/plugin-sharing/vitest.config.ts", "packages/spec/api-surface/data.json", "packages/spec/export-origins/data.json", "packages/spec/src/data/injected-system-column-provenance.test.ts", "packages/spec/src/data/injected-system-column-provenance.ts" ], "mcp_calls": "4 reads, 0 GitHub writes — issue_read (get #27661, get_comments #27661), pull_request_read (get #27658 for the body form); plus claude-code-remote subscribe_pr_activity #27794 (harness default). One unauthenticated REST read of #27583 comments for the report form.", "api_writes": "2 relay writes as objectstack-fleet[bot]: (1) fleet-write pr_create, run 40973266290 = #27794 (draft forced, no assignees, no labels; read-back 11542 bytes sent, 11542 stored, identical); (2) post-stamped --comment=27661, this report. Plus 4 git pushes to claude/project-thread-c3dfhl.", "open_questions": [], "deviations": [ "Report comment: the signature line comes first and os-dev-report second, as post-stamped requires; the role file wants os-dev-report on the first line.", "Two wip commits (no trailer, committed with --no-verify) sit under the final commit on the branch; the branch is not claude/issue-*, so no force-push to squash them. The final commit fe45a398 carries the model-free trailer pair; the queue squash-merges.", "File surface: packages/plugins/plugin-sharing/vitest.config.ts (one comment that described the deleted comparison) is outside the claim's list; it is the package's test config, comment-only.", "PR subscription: subscribed to #27794 as the harness asks; no CI waiting or polling in this round." ], "out_of_scope_findings": [ "carrier: none · noted, not filed — packages/qa/dogfood/test/federated-phantom-share-grant.dogfood.test.ts L251-L256 still calls hasPhantomOwnerAnchor hand-rolled; a historical test comment. Dedupe words: dogfood phantom share grant hand-rolled comment.", "carrier: none · noted, not filed — plugin-security's tenant twin (federated-phantom-anchors.ts, hasPhantomTenantAnchor) still carries its own identity comparison; the convergence row #7859 keeps it opportunistic and it has no columnMap arm. Dedupe words: hasPhantomTenantAnchor convergence equalsShippedDef plugin-security." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Landed · 2026-10-30T00:35Z
PR #27794 merged through the queue as
684f2f652c7a0dbf5f030a6582aa62b35791a602(headdb96a59d; contract review PASS 6511315513 atCONTRACT_REVIEW_TIER, which also served as the full review of the spec change; seat ACCEPT 6511399450).@objectstack/spec/datanow exportsisOwnerAnchorUnprovableandexternalColumnMapTargets, the one reader of "is an external object's owner provable from declared metadata". Plugin-sharing and lint read it. plugin-sharing'sisFederatedObjectstays as a one-line read ofplatformProvisionsStorage, because #27760 imports it. The record's two doc-only should-fixes ride #27803.