Skip to content

spec: resolveInjectedColumnProvenance does not know the external.columnMap → owner_id arm plugin-sharing honours since #27425, so the #7858 convergence row and the two columnMap readers diverge (epic #27261) #27661

Closed

Description

@objectstack-fleet

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

维护者速读

Task

  • One reader decides "owner anchor provable from declared metadata" (a declared owner_id field, or an external.columnMap value owner_id). Either resolveInjectedColumnProvenance takes the columnMap arm, 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 __readScope own/unit to an owner_id predicate on federated objects, where owner_id is a phantom column #7858 in injected-system-column-provenance.ts reads 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

  1. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
    Claim: PM loop round 7 2026-10-29T21:57Z
    Session: session_017ihL2c5z3q4h8inrn5kwqV (epic PM); implementing card thread session_017ihL2eCEdjSZBk7DGrPPFg
    Account: marchtian
    Branch: claude/project-thread-c3dfhl (the card thread's platform-assigned branch — measured platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-27661
    Domain: domain:spec
    Seat: epic:#27261#1 (epic PM; no domain:* 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 its columnMap reader; the #7858 convergence row), packages/plugins/plugin-sharing/src/federated-phantom-anchors.ts (hasPhantomOwnerAnchor reads the spec pair), packages/lint/src/validate-external-owner-sharing.ts (hasUnprovableOwnerAnchor / columnMapTargets read 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: high
    Clause-②: yes
    Metadata-protocol: none
    Responsibility: #27425 (PR #27476) added the columnMap → owner_id arm in plugin-sharing only; review 6493803142 ④ should-fix 1 and seat carry 6493297056
    Thread-read: none
    Premise: holds — on origin/main resolveInjectedColumnProvenance has no columnMap arm while plugin-sharing's mapsRemoteColumnOntoOwner and lint's columnMapTargets each 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 a CONTRACT_REVIEW_TIER record PASS is on its head.

  2. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  3. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
    Landed · 2026-10-30T00:35Z

    PR #27794 merged through the queue as 684f2f652c7a0dbf5f030a6582aa62b35791a602 (head db96a59d; contract review PASS 6511315513 at CONTRACT_REVIEW_TIER, which also served as the full review of the spec change; seat ACCEPT 6511399450). @objectstack/spec/data now exports isOwnerAnchorUnprovable and externalColumnMapTargets, the one reader of "is an external object's owner provable from declared metadata". Plugin-sharing and lint read it. plugin-sharing's isFederatedObject stays as a one-line read of platformProvisionsStorage, because #27760 imports it. The record's two doc-only should-fixes ride #27803.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:specpm:epicpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions